diff options
author | 2016-04-28 16:59:42 -0700 | |
---|---|---|
committer | 2016-05-03 14:36:29 -0700 | |
commit | 138c21fbec12bead3c7ca1f181c3fd35542ccb00 (patch) | |
tree | 44e5b182b83c2532dd02f1ccb6d9c9186a48c4cf | |
parent | 08ca2e3a7593ced4967c56709a1fe675408d42dc (diff) |
Use LUT for computing final shadow alpha
bug:27415250
Significantly reduces shadow fragment shader computation.
Change-Id: Ie9b3c712700754b3734d0ae9cda8751c298fc59e
-rw-r--r-- | libs/hwui/AmbientShadow.cpp | 29 | ||||
-rw-r--r-- | libs/hwui/BakedOpDispatcher.cpp | 5 | ||||
-rw-r--r-- | libs/hwui/Caches.cpp | 1 | ||||
-rw-r--r-- | libs/hwui/Glop.h | 5 | ||||
-rw-r--r-- | libs/hwui/GlopBuilder.cpp | 23 | ||||
-rw-r--r-- | libs/hwui/GlopBuilder.h | 4 | ||||
-rw-r--r-- | libs/hwui/OpenGLRenderer.cpp | 4 | ||||
-rw-r--r-- | libs/hwui/ProgramCache.cpp | 7 | ||||
-rw-r--r-- | libs/hwui/SpotShadow.cpp | 13 | ||||
-rw-r--r-- | libs/hwui/renderstate/RenderState.cpp | 12 | ||||
-rw-r--r-- | libs/hwui/renderstate/TextureState.cpp | 38 | ||||
-rw-r--r-- | libs/hwui/renderstate/TextureState.h | 12 |
12 files changed, 101 insertions, 52 deletions
diff --git a/libs/hwui/AmbientShadow.cpp b/libs/hwui/AmbientShadow.cpp index 20ecda28b22a..3982fa0938bf 100644 --- a/libs/hwui/AmbientShadow.cpp +++ b/libs/hwui/AmbientShadow.cpp @@ -43,9 +43,7 @@ /** * Other constants: */ -// For the edge of the penumbra, the opacity is 0. After transform (1 - alpha), -// it is 1. -#define TRANSFORMED_OUTER_OPACITY (1.0f) +#define OUTER_ALPHA (0.0f) // Once the alpha difference is greater than this threshold, we will allocate extra // edge vertices. @@ -81,17 +79,6 @@ inline float getAlphaFromFactoredZ(float factoredZ) { return 1.0 / (1 + std::max(factoredZ, 0.0f)); } -// The shader is using gaussian function e^-(1-x)*(1-x)*4, therefore, we transform -// the alpha value to (1 - alpha) -inline float getTransformedAlphaFromAlpha(float alpha) { - return 1.0f - alpha; -} - -// The output is ranged from 0 to 1. -inline float getTransformedAlphaFromFactoredZ(float factoredZ) { - return getTransformedAlphaFromAlpha(getAlphaFromFactoredZ(factoredZ)); -} - inline int getEdgeExtraAndUpdateSpike(Vector2* currentSpike, const Vector3& secondVertex, const Vector3& centroid) { Vector2 secondSpike = {secondVertex.x - centroid.x, secondVertex.y - centroid.y}; @@ -225,9 +212,9 @@ void AmbientShadow::createAmbientShadow(bool isCasterOpaque, if (!isCasterOpaque) { umbraVertices[umbraIndex++] = vertexBufferIndex; } - AlphaVertex::set(&shadowVertices[vertexBufferIndex++], casterVertices[i].x, - casterVertices[i].y, - getTransformedAlphaFromAlpha(currentAlpha)); + AlphaVertex::set(&shadowVertices[vertexBufferIndex++], + casterVertices[i].x, casterVertices[i].y, + currentAlpha); const Vector3& innerStart = casterVertices[i]; @@ -249,7 +236,7 @@ void AmbientShadow::createAmbientShadow(bool isCasterOpaque, indexBuffer[indexBufferIndex++] = vertexBufferIndex; indexBuffer[indexBufferIndex++] = currentInnerVertexIndex; AlphaVertex::set(&shadowVertices[vertexBufferIndex++], outerVertex.x, - outerVertex.y, TRANSFORMED_OUTER_OPACITY); + outerVertex.y, OUTER_ALPHA); if (j == 0) { outerStart = outerVertex; @@ -285,7 +272,7 @@ void AmbientShadow::createAmbientShadow(bool isCasterOpaque, (outerLast * startWeight + outerNext * k) / extraVerticesNumber; indexBuffer[indexBufferIndex++] = vertexBufferIndex; AlphaVertex::set(&shadowVertices[vertexBufferIndex++], currentOuter.x, - currentOuter.y, TRANSFORMED_OUTER_OPACITY); + currentOuter.y, OUTER_ALPHA); if (!isCasterOpaque) { umbraVertices[umbraIndex++] = vertexBufferIndex; @@ -295,7 +282,7 @@ void AmbientShadow::createAmbientShadow(bool isCasterOpaque, indexBuffer[indexBufferIndex++] = vertexBufferIndex; AlphaVertex::set(&shadowVertices[vertexBufferIndex++], currentInner.x, currentInner.y, - getTransformedAlphaFromFactoredZ(currentInner.z * heightFactor)); + getAlphaFromFactoredZ(currentInner.z * heightFactor)); } } currentAlpha = nextAlpha; @@ -307,7 +294,7 @@ void AmbientShadow::createAmbientShadow(bool isCasterOpaque, if (!isCasterOpaque) { // Add the centroid as the last one in the vertex buffer. float centroidOpacity = - getTransformedAlphaFromFactoredZ(centroid3d.z * heightFactor); + getAlphaFromFactoredZ(centroid3d.z * heightFactor); int centroidIndex = vertexBufferIndex; AlphaVertex::set(&shadowVertices[vertexBufferIndex++], centroid3d.x, centroid3d.y, centroidOpacity); diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp index 8251ee6698c1..19df68ec98e6 100644 --- a/libs/hwui/BakedOpDispatcher.cpp +++ b/libs/hwui/BakedOpDispatcher.cpp @@ -346,11 +346,12 @@ static void renderVertexBuffer(BakedOpRenderer& renderer, const BakedOpState& st bool shadowInterp = vertexBufferRenderFlags & VertexBufferRenderFlags::ShadowInterp; const int transformFlags = vertexBufferRenderFlags & VertexBufferRenderFlags::Offset ? TransformFlags::OffsetByFudgeFactor : 0; + Glop glop; GlopBuilder(renderer.renderState(), renderer.caches(), &glop) .setRoundRectClipState(state.roundRectClipState) - .setMeshVertexBuffer(vertexBuffer, shadowInterp) - .setFillPaint(paint, state.alpha) + .setMeshVertexBuffer(vertexBuffer) + .setFillPaint(paint, state.alpha, shadowInterp) .setTransform(state.computedState.transform, transformFlags) .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds()) .build(); diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index eaa1c330d5dd..949ad450d5f7 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -74,6 +74,7 @@ bool Caches::init() { mPixelBufferState = new PixelBufferState(); mTextureState = new TextureState(); + mTextureState->constructTexture(*this); return true; } diff --git a/libs/hwui/Glop.h b/libs/hwui/Glop.h index 6a9663412a00..6433c86908e7 100644 --- a/libs/hwui/Glop.h +++ b/libs/hwui/Glop.h @@ -44,9 +44,14 @@ class Texture; namespace VertexAttribFlags { enum { + // Mesh is pure x,y vertex pairs None = 0, + // Mesh has texture coordinates embedded. Note that texture can exist without this flag + // being set, if coordinates passed to sampler are determined another way. TextureCoord = 1 << 0, + // Mesh has color embedded (to export to varying) Color = 1 << 1, + // Mesh has alpha embedded (to export to varying) Alpha = 1 << 2, }; }; diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp index fdbe76a5e459..910300d3f4f6 100644 --- a/libs/hwui/GlopBuilder.cpp +++ b/libs/hwui/GlopBuilder.cpp @@ -193,7 +193,7 @@ GlopBuilder& GlopBuilder::setMeshColoredTexturedMesh(ColorTextureVertex* vertexD return *this; } -GlopBuilder& GlopBuilder::setMeshVertexBuffer(const VertexBuffer& vertexBuffer, bool shadowInterp) { +GlopBuilder& GlopBuilder::setMeshVertexBuffer(const VertexBuffer& vertexBuffer) { TRIGGER_STAGE(kMeshStage); const VertexBuffer::MeshFeatureFlags flags = vertexBuffer.getMeshFeatureFlags(); @@ -210,8 +210,6 @@ GlopBuilder& GlopBuilder::setMeshVertexBuffer(const VertexBuffer& vertexBuffer, alphaVertex ? kAlphaVertexStride : kVertexStride }; mOutGlop->mesh.elementCount = indices ? vertexBuffer.getIndexCount() : vertexBuffer.getVertexCount(); - - mDescription.useShadowAlphaInterp = shadowInterp; return *this; } @@ -368,15 +366,23 @@ GlopBuilder& GlopBuilder::setFillTexturePaint(Texture& texture, return *this; } -GlopBuilder& GlopBuilder::setFillPaint(const SkPaint& paint, float alphaScale) { +GlopBuilder& GlopBuilder::setFillPaint(const SkPaint& paint, float alphaScale, bool shadowInterp) { TRIGGER_STAGE(kFillStage); REQUIRE_STAGES(kMeshStage | kRoundRectClipStage); - mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr }; + if (CC_LIKELY(!shadowInterp)) { + mOutGlop->fill.texture = { + nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr }; + } else { + mOutGlop->fill.texture = { + mCaches.textureState().getShadowLutTexture(), GL_TEXTURE_2D, + GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr }; + } setFill(paint.getColor(), alphaScale, PaintUtils::getXfermode(paint.getXfermode()), Blend::ModeOrderSwap::NoSwap, paint.getShader(), paint.getColorFilter()); + mDescription.useShadowAlphaInterp = shadowInterp; mDescription.modulate = mOutGlop->fill.color.a < 1.0f; return *this; } @@ -592,8 +598,11 @@ GlopBuilder& GlopBuilder::setRoundRectClipState(const RoundRectClipState* roundR 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) - || ((glop.mesh.vertices.attribFlags & VertexAttribFlags::TextureCoord) == 0)), + || (!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, diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h index b6c186d85e76..4f5f0d83becb 100644 --- a/libs/hwui/GlopBuilder.h +++ b/libs/hwui/GlopBuilder.h @@ -51,14 +51,14 @@ public: GlopBuilder& setMeshUnitQuad(); GlopBuilder& setMeshTexturedUnitQuad(const UvMapper* uvMapper); GlopBuilder& setMeshTexturedUvQuad(const UvMapper* uvMapper, const Rect uvs); - GlopBuilder& setMeshVertexBuffer(const VertexBuffer& vertexBuffer, bool shadowInterp); + GlopBuilder& setMeshVertexBuffer(const VertexBuffer& vertexBuffer); GlopBuilder& setMeshIndexedQuads(Vertex* vertexData, int quadCount); GlopBuilder& setMeshTexturedMesh(TextureVertex* vertexData, int elementCount); // TODO: delete GlopBuilder& setMeshColoredTexturedMesh(ColorTextureVertex* vertexData, int elementCount); // TODO: use indexed quads GlopBuilder& setMeshTexturedIndexedQuads(TextureVertex* vertexData, int elementCount); // TODO: take quadCount GlopBuilder& setMeshPatchQuads(const Patch& patch); - GlopBuilder& setFillPaint(const SkPaint& paint, float alphaScale); + GlopBuilder& setFillPaint(const SkPaint& paint, float alphaScale, bool shadowInterp = false); // TODO: avoid boolean with default GlopBuilder& setFillTexturePaint(Texture& texture, const int textureFillFlags, const SkPaint* paint, float alphaScale); GlopBuilder& setFillPathTexturePaint(PathTexture& texture, diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 53ea7fa6f77d..b68240ac7519 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -1697,8 +1697,8 @@ void OpenGLRenderer::drawVertexBuffer(float translateX, float translateY, Glop glop; GlopBuilder(mRenderState, mCaches, &glop) .setRoundRectClipState(currentSnapshot()->roundRectClipState) - .setMeshVertexBuffer(vertexBuffer, shadowInterp) - .setFillPaint(*paint, currentSnapshot()->alpha) + .setMeshVertexBuffer(vertexBuffer) + .setFillPaint(*paint, currentSnapshot()->alpha, shadowInterp) .setTransform(*currentSnapshot(), transformFlags) .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds()) .build(); diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp index 05be48822fb2..59225e108ac7 100644 --- a/libs/hwui/ProgramCache.cpp +++ b/libs/hwui/ProgramCache.cpp @@ -231,9 +231,8 @@ const char* gFS_Main_ModulateColor = const char* gFS_Main_ApplyVertexAlphaLinearInterp = " fragColor *= alpha;\n"; const char* gFS_Main_ApplyVertexAlphaShadowInterp = - // Use a gaussian function for the shadow fall off. Note that alpha here - // is actually (1.0 - alpha) for saving computation. - " fragColor *= exp(- alpha * alpha * 4.0) - 0.018;\n"; + // map alpha through shadow alpha sampler + " fragColor *= texture2D(baseSampler, vec2(alpha, 0.5)).a;\n"; const char* gFS_Main_FetchTexture[2] = { // Don't modulate " fragColor = texture2D(baseSampler, outTexCoords);\n", @@ -565,7 +564,7 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti shader.append(gFS_Uniforms_Color); if (!singleColor) modulateOp = MODULATE_OP_MODULATE; } - if (description.hasTexture) { + if (description.hasTexture || description.useShadowAlphaInterp) { shader.append(gFS_Uniforms_TextureSampler); } else if (description.hasExternalTexture) { shader.append(gFS_Uniforms_ExternalTextureSampler); diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp index 759e39b2e4e2..760d814f27a8 100644 --- a/libs/hwui/SpotShadow.cpp +++ b/libs/hwui/SpotShadow.cpp @@ -42,9 +42,8 @@ // For each RADIANS_DIVISOR, we would allocate one more vertex b/t the normals. #define SPOT_CORNER_RADIANS_DIVISOR (M_PI / SPOT_EXTRA_CORNER_VERTEX_PER_PI) -// For performance, we use (1 - alpha) value for the shader input. -#define TRANSFORMED_PENUMBRA_ALPHA 1.0f -#define TRANSFORMED_UMBRA_ALPHA 0.0f +#define PENUMBRA_ALPHA 0.0f +#define UMBRA_ALPHA 1.0f #include "SpotShadow.h" @@ -941,11 +940,11 @@ void SpotShadow::generateTriangleStrip(bool isCasterOpaque, float shadowStrength // Fill the IB and VB for the penumbra area. for (int i = 0; i < newPenumbraLength; i++) { AlphaVertex::set(&shadowVertices[vertexBufferIndex++], newPenumbra[i].x, - newPenumbra[i].y, TRANSFORMED_PENUMBRA_ALPHA); + newPenumbra[i].y, PENUMBRA_ALPHA); } for (int i = 0; i < umbraLength; i++) { AlphaVertex::set(&shadowVertices[vertexBufferIndex++], umbra[i].x, umbra[i].y, - TRANSFORMED_UMBRA_ALPHA); + UMBRA_ALPHA); } for (int i = 0; i < verticesPairIndex; i++) { @@ -985,14 +984,14 @@ void SpotShadow::generateTriangleStrip(bool isCasterOpaque, float shadowStrength indexBuffer[indexBufferIndex++] = newPenumbraLength + i; indexBuffer[indexBufferIndex++] = vertexBufferIndex; AlphaVertex::set(&shadowVertices[vertexBufferIndex++], - closerVertex.x, closerVertex.y, TRANSFORMED_UMBRA_ALPHA); + closerVertex.x, closerVertex.y, UMBRA_ALPHA); } } else { // If there is no occluded umbra at all, then draw the triangle fan // starting from the centroid to all umbra vertices. int lastCentroidIndex = vertexBufferIndex; AlphaVertex::set(&shadowVertices[vertexBufferIndex++], centroid.x, - centroid.y, TRANSFORMED_UMBRA_ALPHA); + centroid.y, UMBRA_ALPHA); for (int i = 0; i < umbraLength; i++) { indexBuffer[indexBufferIndex++] = newPenumbraLength + i; indexBuffer[indexBufferIndex++] = lastCentroidIndex; diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp index ea4391b87006..e78cd7296f42 100644 --- a/libs/hwui/renderstate/RenderState.cpp +++ b/libs/hwui/renderstate/RenderState.cpp @@ -298,7 +298,8 @@ void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix) { // indices meshState().bindIndicesBuffer(indices.bufferObject); - if (vertices.attribFlags & VertexAttribFlags::TextureCoord) { + // texture + if (fill.texture.texture != nullptr) { const Glop::Fill::TextureData& texture = fill.texture; // texture always takes slot 0, shader samplers increment from there mCaches->textureState().activateTexture(0); @@ -311,13 +312,16 @@ void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix) { texture.texture->setFilter(texture.filter, false, false, texture.target); } - meshState().enableTexCoordsVertexArray(); - meshState().bindTexCoordsVertexPointer(vertices.texCoord, vertices.stride); - if (texture.textureTransform) { glUniformMatrix4fv(fill.program->getUniform("mainTextureTransform"), 1, GL_FALSE, &texture.textureTransform->data[0]); } + } + + // vertex attributes (tex coord, color, alpha) + if (vertices.attribFlags & VertexAttribFlags::TextureCoord) { + meshState().enableTexCoordsVertexArray(); + meshState().bindTexCoordsVertexPointer(vertices.texCoord, vertices.stride); } else { meshState().disableTexCoordsVertexArray(); } diff --git a/libs/hwui/renderstate/TextureState.cpp b/libs/hwui/renderstate/TextureState.cpp index 78b8edae2eed..f9a1f8c38895 100644 --- a/libs/hwui/renderstate/TextureState.cpp +++ b/libs/hwui/renderstate/TextureState.cpp @@ -26,6 +26,9 @@ namespace android { namespace uirenderer { +// Width of mShadowLutTexture, defines how accurate the shadow alpha lookup table is +static const int SHADOW_LUT_SIZE = 128; + // Must define as many texture units as specified by kTextureUnitsCount const GLenum kTextureUnits[] = { GL_TEXTURE0, @@ -46,6 +49,41 @@ TextureState::TextureState() glPixelStorei(GL_UNPACK_ALIGNMENT, 1); } +TextureState::~TextureState() { + if (mShadowLutTexture != nullptr) { + mShadowLutTexture->deleteTexture(); + } +} + +/** + * Maps shadow geometry 'alpha' varying (1 for darkest, 0 for transparent) to + * darkness at that spot. Input values of 0->1 should be mapped within the same + * range, but can affect the curve for a different visual falloff. + * + * This is used to populate the shadow LUT texture for quick lookup in the + * shadow shader. + */ +static float computeShadowOpacity(float ratio) { + // exponential falloff function provided by UX + float val = 1 - ratio; + return exp(-val * val * 4.0) - 0.018; +} + +void TextureState::constructTexture(Caches& caches) { + if (mShadowLutTexture == nullptr) { + mShadowLutTexture.reset(new Texture(caches)); + + unsigned char bytes[SHADOW_LUT_SIZE]; + for (int i = 0; i < SHADOW_LUT_SIZE; i++) { + float inputRatio = i / (SHADOW_LUT_SIZE - 1.0f); + bytes[i] = computeShadowOpacity(inputRatio) * 255; + } + mShadowLutTexture->upload(GL_ALPHA, SHADOW_LUT_SIZE, 1, GL_ALPHA, GL_UNSIGNED_BYTE, &bytes); + mShadowLutTexture->setFilter(GL_LINEAR); + mShadowLutTexture->setWrap(GL_CLAMP_TO_EDGE); + } +} + void TextureState::activateTexture(GLuint textureUnit) { LOG_ALWAYS_FATAL_IF(textureUnit >= kTextureUnitsCount, "Tried to use texture unit index %d, only %d exist", diff --git a/libs/hwui/renderstate/TextureState.h b/libs/hwui/renderstate/TextureState.h index ec94d7e9e267..7296fd39a705 100644 --- a/libs/hwui/renderstate/TextureState.h +++ b/libs/hwui/renderstate/TextureState.h @@ -17,14 +17,12 @@ #define RENDERSTATE_TEXTURESTATE_H #include "Vertex.h" +#include "Texture.h" #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> -#include <SkXfermode.h> #include <memory> -class SkBitmap; - namespace android { namespace uirenderer { @@ -33,6 +31,9 @@ class Texture; class TextureState { friend class Caches; // TODO: move to RenderState public: + + void constructTexture(Caches& caches); + /** * Activate the specified texture unit. The texture unit must * be specified using an integer number (0 for GL_TEXTURE0 etc.) @@ -76,15 +77,20 @@ public: */ void unbindTexture(GLuint texture); + Texture* getShadowLutTexture() { return mShadowLutTexture.get(); } + private: // total number of texture units available for use static const int kTextureUnitsCount = 4; TextureState(); + ~TextureState(); GLuint mTextureUnit; // Caches texture bindings for the GL_TEXTURE_2D target GLuint mBoundTextures[kTextureUnitsCount]; + + std::unique_ptr<Texture> mShadowLutTexture; }; } /* namespace uirenderer */ |