Romain Guy 636afc1877 Apply transfer function when rendering with linear textures
RGBA16F bitmaps are always encoded in linear space, which means we must
apply the opto-electronic transfer function before we can render them
in the framebuffer.

Since our linear bitmaps are assumed to be scRGB, values can be negative.
The OETF is a slightly modified sRGB OETF:

sign(x) * OETF_sRGB(abs(x))

This effectively mirrors the OETF over the negative domain.

This CL also removes the "optimized" shader generation path. With
current compilers, the optimized path doesn't do anything of value
and makes ProgramCache difficult to maintain. Shader compilers inline
everything and are really good at folding expressions and removing
unused code.

Bug: 32984164
Test: CtsUiRenderingTestCases
Change-Id: Ieb458ad53574e3a8959aa6bccbbd2d1fe203cbc5
2017-02-07 22:30:34 +00:00

694 lines
27 KiB
C++

/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "GlopBuilder.h"
#include "Caches.h"
#include "GlLayer.h"
#include "Glop.h"
#include "Layer.h"
#include "Matrix.h"
#include "Patch.h"
#include "PathCache.h"
#include "renderstate/MeshState.h"
#include "renderstate/RenderState.h"
#include "SkiaShader.h"
#include "Texture.h"
#include "utils/PaintUtils.h"
#include "VertexBuffer.h"
#include <GLES2/gl2.h>
#include <SkPaint.h>
#define DEBUG_GLOP_BUILDER 0
#if DEBUG_GLOP_BUILDER
#define TRIGGER_STAGE(stageFlag) \
LOG_ALWAYS_FATAL_IF((stageFlag) & mStageFlags, "Stage %d cannot be run twice", (stageFlag)); \
mStageFlags = static_cast<StageFlags>(mStageFlags | (stageFlag))
#define REQUIRE_STAGES(requiredFlags) \
LOG_ALWAYS_FATAL_IF((mStageFlags & (requiredFlags)) != (requiredFlags), \
"not prepared for current stage")
#else
#define TRIGGER_STAGE(stageFlag) ((void)0)
#define REQUIRE_STAGES(requiredFlags) ((void)0)
#endif
namespace android {
namespace uirenderer {
static void setUnitQuadTextureCoords(Rect uvs, TextureVertex* quadVertex) {
quadVertex[0] = {0, 0, uvs.left, uvs.top};
quadVertex[1] = {1, 0, uvs.right, uvs.top};
quadVertex[2] = {0, 1, uvs.left, uvs.bottom};
quadVertex[3] = {1, 1, uvs.right, uvs.bottom};
}
GlopBuilder::GlopBuilder(RenderState& renderState, Caches& caches, Glop* outGlop)
: mRenderState(renderState)
, mCaches(caches)
, mShader(nullptr)
, mOutGlop(outGlop) {
mStageFlags = kInitialStage;
}
////////////////////////////////////////////////////////////////////////////////
// Mesh
////////////////////////////////////////////////////////////////////////////////
GlopBuilder& GlopBuilder::setMeshTexturedIndexedVbo(GLuint vbo, GLsizei elementCount) {
TRIGGER_STAGE(kMeshStage);
mOutGlop->mesh.primitiveMode = GL_TRIANGLES;
mOutGlop->mesh.indices = { mRenderState.meshState().getQuadListIBO(), nullptr };
mOutGlop->mesh.vertices = {
vbo,
VertexAttribFlags::TextureCoord,
nullptr, (const void*) kMeshTextureOffset, nullptr,
kTextureVertexStride };
mOutGlop->mesh.elementCount = elementCount;
return *this;
}
GlopBuilder& GlopBuilder::setMeshUnitQuad() {
TRIGGER_STAGE(kMeshStage);
mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP;
mOutGlop->mesh.indices = { 0, nullptr };
mOutGlop->mesh.vertices = {
mRenderState.meshState().getUnitQuadVBO(),
VertexAttribFlags::None,
nullptr, nullptr, nullptr,
kTextureVertexStride };
mOutGlop->mesh.elementCount = 4;
return *this;
}
GlopBuilder& GlopBuilder::setMeshTexturedUnitQuad(const UvMapper* uvMapper) {
if (uvMapper) {
// can't use unit quad VBO, so build UV vertices manually
return setMeshTexturedUvQuad(uvMapper, Rect(1, 1));
}
TRIGGER_STAGE(kMeshStage);
mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP;
mOutGlop->mesh.indices = { 0, nullptr };
mOutGlop->mesh.vertices = {
mRenderState.meshState().getUnitQuadVBO(),
VertexAttribFlags::TextureCoord,
nullptr, (const void*) kMeshTextureOffset, nullptr,
kTextureVertexStride };
mOutGlop->mesh.elementCount = 4;
return *this;
}
GlopBuilder& GlopBuilder::setMeshTexturedUvQuad(const UvMapper* uvMapper, Rect uvs) {
TRIGGER_STAGE(kMeshStage);
if (CC_UNLIKELY(uvMapper)) {
uvMapper->map(uvs);
}
setUnitQuadTextureCoords(uvs, &mOutGlop->mesh.mappedVertices[0]);
const TextureVertex* textureVertex = mOutGlop->mesh.mappedVertices;
mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP;
mOutGlop->mesh.indices = { 0, nullptr };
mOutGlop->mesh.vertices = {
0,
VertexAttribFlags::TextureCoord,
&textureVertex[0].x, &textureVertex[0].u, nullptr,
kTextureVertexStride };
mOutGlop->mesh.elementCount = 4;
return *this;
}
GlopBuilder& GlopBuilder::setMeshIndexedQuads(Vertex* vertexData, int quadCount) {
TRIGGER_STAGE(kMeshStage);
mOutGlop->mesh.primitiveMode = GL_TRIANGLES;
mOutGlop->mesh.indices = { mRenderState.meshState().getQuadListIBO(), nullptr };
mOutGlop->mesh.vertices = {
0,
VertexAttribFlags::None,
vertexData, nullptr, nullptr,
kVertexStride };
mOutGlop->mesh.elementCount = 6 * quadCount;
return *this;
}
GlopBuilder& GlopBuilder::setMeshTexturedIndexedQuads(TextureVertex* vertexData, int elementCount) {
TRIGGER_STAGE(kMeshStage);
mOutGlop->mesh.primitiveMode = GL_TRIANGLES;
mOutGlop->mesh.indices = { mRenderState.meshState().getQuadListIBO(), nullptr };
mOutGlop->mesh.vertices = {
0,
VertexAttribFlags::TextureCoord,
&vertexData[0].x, &vertexData[0].u, nullptr,
kTextureVertexStride };
mOutGlop->mesh.elementCount = elementCount;
return *this;
}
GlopBuilder& GlopBuilder::setMeshColoredTexturedMesh(ColorTextureVertex* vertexData, int elementCount) {
TRIGGER_STAGE(kMeshStage);
mOutGlop->mesh.primitiveMode = GL_TRIANGLES;
mOutGlop->mesh.indices = { 0, nullptr };
mOutGlop->mesh.vertices = {
0,
VertexAttribFlags::TextureCoord | VertexAttribFlags::Color,
&vertexData[0].x, &vertexData[0].u, &vertexData[0].r,
kColorTextureVertexStride };
mOutGlop->mesh.elementCount = elementCount;
return *this;
}
GlopBuilder& GlopBuilder::setMeshVertexBuffer(const VertexBuffer& vertexBuffer) {
TRIGGER_STAGE(kMeshStage);
const VertexBuffer::MeshFeatureFlags flags = vertexBuffer.getMeshFeatureFlags();
bool alphaVertex = flags & VertexBuffer::kAlpha;
bool indices = flags & VertexBuffer::kIndices;
mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP;
mOutGlop->mesh.indices = { 0, vertexBuffer.getIndices() };
mOutGlop->mesh.vertices = {
0,
alphaVertex ? VertexAttribFlags::Alpha : VertexAttribFlags::None,
vertexBuffer.getBuffer(), nullptr, nullptr,
alphaVertex ? kAlphaVertexStride : kVertexStride };
mOutGlop->mesh.elementCount = indices
? vertexBuffer.getIndexCount() : vertexBuffer.getVertexCount();
return *this;
}
GlopBuilder& GlopBuilder::setMeshPatchQuads(const Patch& patch) {
TRIGGER_STAGE(kMeshStage);
mOutGlop->mesh.primitiveMode = GL_TRIANGLES;
mOutGlop->mesh.indices = { mRenderState.meshState().getQuadListIBO(), nullptr };
mOutGlop->mesh.vertices = {
mCaches.patchCache.getMeshBuffer(),
VertexAttribFlags::TextureCoord,
(void*)patch.positionOffset, (void*)patch.textureOffset, nullptr,
kTextureVertexStride };
mOutGlop->mesh.elementCount = patch.indexCount;
return *this;
}
////////////////////////////////////////////////////////////////////////////////
// Fill
////////////////////////////////////////////////////////////////////////////////
void GlopBuilder::setFill(int color, float alphaScale,
SkBlendMode mode, Blend::ModeOrderSwap modeUsage,
const SkShader* shader, const SkColorFilter* colorFilter) {
if (mode != SkBlendMode::kClear) {
if (!shader) {
FloatColor c;
c.set(color);
c.r *= alphaScale;
c.g *= alphaScale;
c.b *= alphaScale;
c.a *= alphaScale;
mOutGlop->fill.color = c;
} else {
float alpha = (SkColorGetA(color) / 255.0f) * alphaScale;
mOutGlop->fill.color = { 1, 1, 1, alpha };
}
} else {
mOutGlop->fill.color = { 0, 0, 0, 1 };
}
mOutGlop->blend = { GL_ZERO, GL_ZERO };
if (mOutGlop->fill.color.a < 1.0f
|| (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::Alpha)
|| (mOutGlop->fill.texture.texture && mOutGlop->fill.texture.texture->blend)
|| mOutGlop->roundRectClipState
|| PaintUtils::isBlendedShader(shader)
|| PaintUtils::isBlendedColorFilter(colorFilter)
|| mode != SkBlendMode::kSrcOver) {
if (CC_LIKELY(mode <= SkBlendMode::kScreen)) {
Blend::getFactors(mode, modeUsage,
&mOutGlop->blend.src, &mOutGlop->blend.dst);
} else {
// These blend modes are not supported by OpenGL directly and have
// to be implemented using shaders. Since the shader will perform
// the blending, don't enable GL blending off here
// If the blend mode cannot be implemented using shaders, fall
// back to the default SrcOver blend mode instead
if (CC_UNLIKELY(mCaches.extensions().hasFramebufferFetch())) {
mDescription.framebufferMode = mode;
mDescription.swapSrcDst = (modeUsage == Blend::ModeOrderSwap::Swap);
// blending in shader, don't enable
} else {
// unsupported
Blend::getFactors(SkBlendMode::kSrcOver, modeUsage,
&mOutGlop->blend.src, &mOutGlop->blend.dst);
}
}
}
mShader = shader; // shader resolved in ::build()
if (colorFilter) {
SkColor color;
SkBlendMode bmode;
SkScalar srcColorMatrix[20];
if (colorFilter->asColorMode(&color, &bmode)) {
mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::ColorFilterMode::Blend;
mDescription.colorMode = bmode;
mOutGlop->fill.filter.color.set(color);
} else if (colorFilter->asColorMatrix(srcColorMatrix)) {
mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::ColorFilterMode::Matrix;
float* colorMatrix = mOutGlop->fill.filter.matrix.matrix;
memcpy(colorMatrix, srcColorMatrix, 4 * sizeof(float));
memcpy(&colorMatrix[4], &srcColorMatrix[5], 4 * sizeof(float));
memcpy(&colorMatrix[8], &srcColorMatrix[10], 4 * sizeof(float));
memcpy(&colorMatrix[12], &srcColorMatrix[15], 4 * sizeof(float));
// Skia uses the range [0..255] for the addition vector, but we need
// the [0..1] range to apply the vector in GLSL
float* colorVector = mOutGlop->fill.filter.matrix.vector;
colorVector[0] = EOCF(srcColorMatrix[4] / 255.0f);
colorVector[1] = EOCF(srcColorMatrix[9] / 255.0f);
colorVector[2] = EOCF(srcColorMatrix[14] / 255.0f);
colorVector[3] = srcColorMatrix[19] / 255.0f; // alpha is linear
} else {
LOG_ALWAYS_FATAL("unsupported ColorFilter");
}
} else {
mOutGlop->fill.filterMode = ProgramDescription::ColorFilterMode::None;
}
}
GlopBuilder& GlopBuilder::setFillTexturePaint(Texture& texture,
const int textureFillFlags, const SkPaint* paint, float alphaScale) {
TRIGGER_STAGE(kFillStage);
REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
GLenum filter = (textureFillFlags & TextureFillFlags::ForceFilter)
? GL_LINEAR : PaintUtils::getFilter(paint);
mOutGlop->fill.texture = { &texture, filter, GL_CLAMP_TO_EDGE, nullptr };
if (paint) {
int color = paint->getColor();
SkShader* shader = paint->getShader();
if (!(textureFillFlags & TextureFillFlags::IsAlphaMaskTexture)) {
// Texture defines color, so disable shaders, and reset all non-alpha color channels
color |= 0x00FFFFFF;
shader = nullptr;
}
setFill(color, alphaScale,
paint->getBlendMode(), Blend::ModeOrderSwap::NoSwap,
shader, paint->getColorFilter());
} else {
mOutGlop->fill.color = { alphaScale, alphaScale, alphaScale, alphaScale };
if (alphaScale < 1.0f
|| (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::Alpha)
|| texture.blend
|| mOutGlop->roundRectClipState) {
Blend::getFactors(SkBlendMode::kSrcOver, Blend::ModeOrderSwap::NoSwap,
&mOutGlop->blend.src, &mOutGlop->blend.dst);
} else {
mOutGlop->blend = { GL_ZERO, GL_ZERO };
}
}
if (textureFillFlags & TextureFillFlags::IsAlphaMaskTexture) {
mDescription.modulate = mOutGlop->fill.color.isNotBlack();
mDescription.hasAlpha8Texture = true;
} else {
mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
}
return *this;
}
GlopBuilder& GlopBuilder::setFillPaint(const SkPaint& paint, float alphaScale, bool shadowInterp) {
TRIGGER_STAGE(kFillStage);
REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
if (CC_LIKELY(!shadowInterp)) {
mOutGlop->fill.texture = {
nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
} else {
mOutGlop->fill.texture = {
mCaches.textureState().getShadowLutTexture(),
GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
}
setFill(paint.getColor(), alphaScale,
paint.getBlendMode(), Blend::ModeOrderSwap::NoSwap,
paint.getShader(), paint.getColorFilter());
mDescription.useShadowAlphaInterp = shadowInterp;
mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
return *this;
}
GlopBuilder& GlopBuilder::setFillPathTexturePaint(PathTexture& texture,
const SkPaint& paint, float alphaScale) {
TRIGGER_STAGE(kFillStage);
REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
//specify invalid filter/clamp, since these are always static for PathTextures
mOutGlop->fill.texture = { &texture, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
setFill(paint.getColor(), alphaScale,
paint.getBlendMode(), Blend::ModeOrderSwap::NoSwap,
paint.getShader(), paint.getColorFilter());
mDescription.hasAlpha8Texture = true;
mDescription.modulate = mOutGlop->fill.color.isNotBlack();
return *this;
}
GlopBuilder& GlopBuilder::setFillShadowTexturePaint(ShadowTexture& texture, int shadowColor,
const SkPaint& paint, float alphaScale) {
TRIGGER_STAGE(kFillStage);
REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
//specify invalid filter/clamp, since these are always static for ShadowTextures
mOutGlop->fill.texture = { &texture, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
const int ALPHA_BITMASK = SK_ColorBLACK;
const int COLOR_BITMASK = ~ALPHA_BITMASK;
if ((shadowColor & ALPHA_BITMASK) == ALPHA_BITMASK) {
// shadow color is fully opaque: override its alpha with that of paint
shadowColor &= paint.getColor() | COLOR_BITMASK;
}
setFill(shadowColor, alphaScale,
paint.getBlendMode(), Blend::ModeOrderSwap::NoSwap,
paint.getShader(), paint.getColorFilter());
mDescription.hasAlpha8Texture = true;
mDescription.modulate = mOutGlop->fill.color.isNotBlack();
return *this;
}
GlopBuilder& GlopBuilder::setFillBlack() {
TRIGGER_STAGE(kFillStage);
REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
setFill(SK_ColorBLACK, 1.0f, SkBlendMode::kSrcOver, Blend::ModeOrderSwap::NoSwap,
nullptr, nullptr);
return *this;
}
GlopBuilder& GlopBuilder::setFillClear() {
TRIGGER_STAGE(kFillStage);
REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
setFill(SK_ColorBLACK, 1.0f, SkBlendMode::kClear, Blend::ModeOrderSwap::NoSwap,
nullptr, nullptr);
return *this;
}
GlopBuilder& GlopBuilder::setFillLayer(Texture& texture, const SkColorFilter* colorFilter,
float alpha, SkBlendMode mode, Blend::ModeOrderSwap modeUsage) {
TRIGGER_STAGE(kFillStage);
REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
mOutGlop->fill.texture = { &texture, GL_LINEAR, GL_CLAMP_TO_EDGE, nullptr };
setFill(SK_ColorWHITE, alpha, mode, modeUsage, nullptr, colorFilter);
mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
return *this;
}
GlopBuilder& GlopBuilder::setFillTextureLayer(GlLayer& layer, float alpha) {
TRIGGER_STAGE(kFillStage);
REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
mOutGlop->fill.texture = { &(layer.getTexture()),
GL_LINEAR, GL_CLAMP_TO_EDGE, &layer.getTexTransform() };
setFill(SK_ColorWHITE, alpha, layer.getMode(), Blend::ModeOrderSwap::NoSwap,
nullptr, layer.getColorFilter());
mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
mDescription.hasTextureTransform = true;
return *this;
}
GlopBuilder& GlopBuilder::setFillExternalTexture(Texture& texture, Matrix4& textureTransform) {
TRIGGER_STAGE(kFillStage);
REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
mOutGlop->fill.texture = { &texture, GL_LINEAR, GL_CLAMP_TO_EDGE, &textureTransform };
setFill(SK_ColorWHITE, 1.0f, SkBlendMode::kSrc, Blend::ModeOrderSwap::NoSwap,
nullptr, nullptr);
mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
mDescription.hasTextureTransform = true;
return *this;
}
GlopBuilder& GlopBuilder::setGammaCorrection(bool enabled) {
REQUIRE_STAGES(kFillStage);
mDescription.hasGammaCorrection = enabled;
return *this;
}
////////////////////////////////////////////////////////////////////////////////
// Transform
////////////////////////////////////////////////////////////////////////////////
GlopBuilder& GlopBuilder::setTransform(const Matrix4& canvas, const int transformFlags) {
TRIGGER_STAGE(kTransformStage);
mOutGlop->transform.canvas = canvas;
mOutGlop->transform.transformFlags = transformFlags;
return *this;
}
////////////////////////////////////////////////////////////////////////////////
// ModelView
////////////////////////////////////////////////////////////////////////////////
GlopBuilder& GlopBuilder::setModelViewMapUnitToRect(const Rect destination) {
TRIGGER_STAGE(kModelViewStage);
mOutGlop->transform.modelView.loadTranslate(destination.left, destination.top, 0.0f);
mOutGlop->transform.modelView.scale(destination.getWidth(), destination.getHeight(), 1.0f);
return *this;
}
GlopBuilder& GlopBuilder::setModelViewMapUnitToRectSnap(const Rect destination) {
TRIGGER_STAGE(kModelViewStage);
REQUIRE_STAGES(kTransformStage | kFillStage);
float left = destination.left;
float top = destination.top;
const Matrix4& meshTransform = mOutGlop->transform.meshTransform();
if (CC_LIKELY(meshTransform.isPureTranslate())) {
// snap by adjusting the model view matrix
const float translateX = meshTransform.getTranslateX();
const float translateY = meshTransform.getTranslateY();
left = (int) floorf(left + translateX + 0.5f) - translateX;
top = (int) floorf(top + translateY + 0.5f) - translateY;
mOutGlop->fill.texture.filter = GL_NEAREST;
}
mOutGlop->transform.modelView.loadTranslate(left, top, 0.0f);
mOutGlop->transform.modelView.scale(destination.getWidth(), destination.getHeight(), 1.0f);
return *this;
}
GlopBuilder& GlopBuilder::setModelViewOffsetRect(float offsetX, float offsetY, const Rect source) {
TRIGGER_STAGE(kModelViewStage);
mOutGlop->transform.modelView.loadTranslate(offsetX, offsetY, 0.0f);
return *this;
}
GlopBuilder& GlopBuilder::setModelViewOffsetRectSnap(float offsetX, float offsetY, const Rect source) {
TRIGGER_STAGE(kModelViewStage);
REQUIRE_STAGES(kTransformStage | kFillStage);
const Matrix4& meshTransform = mOutGlop->transform.meshTransform();
if (CC_LIKELY(meshTransform.isPureTranslate())) {
// snap by adjusting the model view matrix
const float translateX = meshTransform.getTranslateX();
const float translateY = meshTransform.getTranslateY();
offsetX = (int) floorf(offsetX + translateX + source.left + 0.5f) - translateX - source.left;
offsetY = (int) floorf(offsetY + translateY + source.top + 0.5f) - translateY - source.top;
mOutGlop->fill.texture.filter = GL_NEAREST;
}
mOutGlop->transform.modelView.loadTranslate(offsetX, offsetY, 0.0f);
return *this;
}
////////////////////////////////////////////////////////////////////////////////
// RoundRectClip
////////////////////////////////////////////////////////////////////////////////
GlopBuilder& GlopBuilder::setRoundRectClipState(const RoundRectClipState* roundRectClipState) {
TRIGGER_STAGE(kRoundRectClipStage);
mOutGlop->roundRectClipState = roundRectClipState;
mDescription.hasRoundRectClip = roundRectClipState != nullptr;
return *this;
}
////////////////////////////////////////////////////////////////////////////////
// Build
////////////////////////////////////////////////////////////////////////////////
void verify(const ProgramDescription& description, const Glop& glop) {
if (glop.fill.texture.texture != nullptr) {
LOG_ALWAYS_FATAL_IF(((description.hasTexture && description.hasExternalTexture)
|| (!description.hasTexture
&& !description.hasExternalTexture
&& !description.useShadowAlphaInterp)
|| ((glop.mesh.vertices.attribFlags & VertexAttribFlags::TextureCoord) == 0
&& !description.useShadowAlphaInterp)),
"Texture %p, hT%d, hET %d, attribFlags %x",
glop.fill.texture.texture,
description.hasTexture, description.hasExternalTexture,
glop.mesh.vertices.attribFlags);
} else {
LOG_ALWAYS_FATAL_IF((description.hasTexture
|| description.hasExternalTexture
|| ((glop.mesh.vertices.attribFlags & VertexAttribFlags::TextureCoord) != 0)),
"No texture, hT%d, hET %d, attribFlags %x",
description.hasTexture, description.hasExternalTexture,
glop.mesh.vertices.attribFlags);
}
if ((glop.mesh.vertices.attribFlags & VertexAttribFlags::Alpha)
&& glop.mesh.vertices.bufferObject) {
LOG_ALWAYS_FATAL("VBO and alpha attributes are not currently compatible");
}
if (description.hasTextureTransform != (glop.fill.texture.textureTransform != nullptr)) {
LOG_ALWAYS_FATAL("Texture transform incorrectly specified");
}
}
void GlopBuilder::build() {
REQUIRE_STAGES(kAllStages);
if (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::TextureCoord) {
if (mOutGlop->fill.texture.texture->target() == GL_TEXTURE_2D) {
mDescription.hasTexture = true;
} else {
mDescription.hasExternalTexture = true;
}
mDescription.hasLinearTexture = mOutGlop->fill.texture.texture->isLinear();
}
mDescription.hasColors = mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::Color;
mDescription.hasVertexAlpha = mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::Alpha;
// Enable debug highlight when what we're about to draw is tested against
// the stencil buffer and if stencil highlight debugging is on
mDescription.hasDebugHighlight = !Properties::debugOverdraw
&& Properties::debugStencilClip == StencilClipDebug::ShowHighlight
&& mRenderState.stencil().isTestEnabled();
// serialize shader info into ShaderData
GLuint textureUnit = mOutGlop->fill.texture.texture ? 1 : 0;
if (CC_LIKELY(!mShader)) {
mOutGlop->fill.skiaShaderData.skiaShaderType = kNone_SkiaShaderType;
} else {
Matrix4 shaderMatrix;
if (mOutGlop->transform.transformFlags & TransformFlags::MeshIgnoresCanvasTransform) {
// canvas level transform was built into the modelView and geometry,
// so the shader matrix must reverse this
shaderMatrix.loadInverse(mOutGlop->transform.canvas);
shaderMatrix.multiply(mOutGlop->transform.modelView);
} else {
shaderMatrix = mOutGlop->transform.modelView;
}
SkiaShader::store(mCaches, *mShader, shaderMatrix,
&textureUnit, &mDescription, &(mOutGlop->fill.skiaShaderData));
}
// duplicates ProgramCache's definition of color uniform presence
const bool singleColor = !mDescription.hasTexture
&& !mDescription.hasExternalTexture
&& !mDescription.hasGradient
&& !mDescription.hasBitmap;
mOutGlop->fill.colorEnabled = mDescription.modulate || singleColor;
verify(mDescription, *mOutGlop);
// Final step: populate program and map bounds into render target space
mOutGlop->fill.program = mCaches.programCache.get(mDescription);
}
void GlopBuilder::dump(const Glop& glop) {
ALOGD("Glop Mesh");
const Glop::Mesh& mesh = glop.mesh;
ALOGD(" primitive mode: %d", mesh.primitiveMode);
ALOGD(" indices: buffer obj %x, indices %p", mesh.indices.bufferObject, mesh.indices.indices);
const Glop::Mesh::Vertices& vertices = glop.mesh.vertices;
ALOGD(" vertices: buffer obj %x, flags %x, pos %p, tex %p, clr %p, stride %d",
vertices.bufferObject, vertices.attribFlags,
vertices.position, vertices.texCoord, vertices.color, vertices.stride);
ALOGD(" element count: %d", mesh.elementCount);
ALOGD("Glop Fill");
const Glop::Fill& fill = glop.fill;
ALOGD(" program %p", fill.program);
if (fill.texture.texture) {
ALOGD(" texture %p, target %d, filter %d, clamp %d",
fill.texture.texture, fill.texture.texture->target(),
fill.texture.filter, fill.texture.clamp);
if (fill.texture.textureTransform) {
fill.texture.textureTransform->dump("texture transform");
}
}
ALOGD_IF(fill.colorEnabled, " color (argb) %.2f %.2f %.2f %.2f",
fill.color.a, fill.color.r, fill.color.g, fill.color.b);
ALOGD_IF(fill.filterMode != ProgramDescription::ColorFilterMode::None,
" filterMode %d", (int)fill.filterMode);
ALOGD_IF(fill.skiaShaderData.skiaShaderType, " shader type %d",
fill.skiaShaderData.skiaShaderType);
ALOGD("Glop transform");
glop.transform.modelView.dump(" model view");
glop.transform.canvas.dump(" canvas");
ALOGD_IF(glop.transform.transformFlags, " transformFlags 0x%x", glop.transform.transformFlags);
ALOGD_IF(glop.roundRectClipState, "Glop RRCS %p", glop.roundRectClipState);
ALOGD("Glop blend %d %d", glop.blend.src, glop.blend.dst);
}
} /* namespace uirenderer */
} /* namespace android */