diff options
author | Chris Craik <ccraik@google.com> | 2015-02-05 10:12:38 -0800 |
---|---|---|
committer | Chris Craik <ccraik@google.com> | 2015-02-06 13:42:25 -0800 |
commit | 117bdbcfa3e8306dad21e7e01fa71b00cdfa7265 (patch) | |
tree | aa54e4a5538818cd946bbd6e7eb771b83d0cfebd /libs/hwui | |
parent | 34725687748cc2b4ace2bdb49becfdcd569e9a5d (diff) | |
download | frameworks_base-117bdbcfa3e8306dad21e7e01fa71b00cdfa7265.zip frameworks_base-117bdbcfa3e8306dad21e7e01fa71b00cdfa7265.tar.gz frameworks_base-117bdbcfa3e8306dad21e7e01fa71b00cdfa7265.tar.bz2 |
Glop ColorFilter & VertexBuffer support, initial enable
Enables Glop rendering for supported Rects and VertexBuffers
Also removes unused Query object
Change-Id: Ibe227bc362685a153159f75077664f0947764e06
Diffstat (limited to 'libs/hwui')
39 files changed, 443 insertions, 415 deletions
diff --git a/libs/hwui/AmbientShadow.cpp b/libs/hwui/AmbientShadow.cpp index 6ec42c2..0a210d6 100644 --- a/libs/hwui/AmbientShadow.cpp +++ b/libs/hwui/AmbientShadow.cpp @@ -179,7 +179,7 @@ inline bool needsExtraForEdge(float firstAlpha, float secondAlpha) { void AmbientShadow::createAmbientShadow(bool isCasterOpaque, const Vector3* casterVertices, int casterVertexCount, const Vector3& centroid3d, float heightFactor, float geomFactor, VertexBuffer& shadowVertexBuffer) { - shadowVertexBuffer.setMode(VertexBuffer::kIndices); + shadowVertexBuffer.setMeshFeatureFlags(VertexBuffer::kAlpha | VertexBuffer::kIndices); // In order to computer the outer vertices in one loop, we need pre-compute // the normal by the vertex (n - 1) to vertex 0, and the spike and alpha value diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index af1b1cd..f4fc068 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -48,10 +48,11 @@ Caches* Caches::sInstance = nullptr; /////////////////////////////////////////////////////////////////////////////// Caches::Caches(RenderState& renderState) - : patchCache(renderState) + : gradientCache(mExtensions) + , patchCache(renderState) + , programCache(mExtensions) , dither(*this) , mRenderState(&renderState) - , mExtensions(Extensions::getInstance()) , mInitialized(false) { INIT_LOGD("Creating OpenGL renderer caches"); init(); @@ -187,9 +188,9 @@ bool Caches::initProperties() { INIT_LOGD(" Draw reorder enabled"); } - return (prevDebugLayersUpdates != debugLayersUpdates) || - (prevDebugOverdraw != debugOverdraw) || - (prevDebugStencilClip != debugStencilClip); + return (prevDebugLayersUpdates != debugLayersUpdates) + || (prevDebugOverdraw != debugOverdraw) + || (prevDebugStencilClip != debugStencilClip); } void Caches::terminate() { diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h index 16e2058..18bb5e6 100644 --- a/libs/hwui/Caches.h +++ b/libs/hwui/Caches.h @@ -178,14 +178,18 @@ public: kStencilShowRegion }; StencilClipDebug debugStencilClip; - +private: + // Declared before gradientCache and programCache which need this to initialize. + // TODO: cleanup / move elsewhere + Extensions mExtensions; +public: TextureCache textureCache; LayerCache layerCache; RenderBufferCache renderBufferCache; GradientCache gradientCache; - ProgramCache programCache; - PathCache pathCache; PatchCache patchCache; + PathCache pathCache; + ProgramCache programCache; TessellationCache tessellationCache; TextDropShadowCache dropShadowCache; FboCache fboCache; @@ -220,6 +224,7 @@ public: void setProgram(const ProgramDescription& description); void setProgram(Program* program); + Extensions& extensions() { return mExtensions; } Program& program() { return *mProgram; } PixelBufferState& pixelBufferState() { return *mPixelBufferState; } TextureState& textureState() { return *mTextureState; } @@ -248,7 +253,6 @@ private: } RenderState* mRenderState; - Extensions& mExtensions; // Used to render layers std::unique_ptr<TextureVertex[]> mRegionMesh; diff --git a/libs/hwui/Dither.cpp b/libs/hwui/Dither.cpp index 359c193..1ba6511 100644 --- a/libs/hwui/Dither.cpp +++ b/libs/hwui/Dither.cpp @@ -32,8 +32,6 @@ Dither::Dither(Caches& caches) void Dither::bindDitherTexture() { if (!mInitialized) { - bool useFloatTexture = Extensions::getInstance().hasFloatTextures(); - glGenTextures(1, &mDitherTexture); mCaches.textureState().bindTexture(mDitherTexture); @@ -43,7 +41,7 @@ void Dither::bindDitherTexture() { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - if (useFloatTexture) { + if (mCaches.extensions().hasFloatTextures()) { // We use a R16F texture, let's remap the alpha channel to the // red channel to avoid changing the shader sampling code on GL ES 3.0+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_RED); diff --git a/libs/hwui/Dither.h b/libs/hwui/Dither.h index facd1ea..b589b80 100644 --- a/libs/hwui/Dither.h +++ b/libs/hwui/Dither.h @@ -23,6 +23,7 @@ namespace android { namespace uirenderer { class Caches; +class Extensions; class Program; // Must be a power of two diff --git a/libs/hwui/Extensions.cpp b/libs/hwui/Extensions.cpp index 8352c3f..c68822b 100644 --- a/libs/hwui/Extensions.cpp +++ b/libs/hwui/Extensions.cpp @@ -16,18 +16,16 @@ #define LOG_TAG "OpenGLRenderer" -#include <GLES2/gl2.h> -#include <GLES2/gl2ext.h> +#include "Extensions.h" + +#include "Debug.h" +#include "Properties.h" #include <EGL/egl.h> #include <EGL/eglext.h> - +#include <GLES2/gl2ext.h> #include <utils/Log.h> -#include "Debug.h" -#include "Extensions.h" -#include "Properties.h" - namespace android { using namespace uirenderer; @@ -50,7 +48,7 @@ namespace uirenderer { // Constructors /////////////////////////////////////////////////////////////////////////////// -Extensions::Extensions(): Singleton<Extensions>() { +Extensions::Extensions() { // Query GL extensions findExtensions((const char*) glGetString(GL_EXTENSIONS), mGlExtensionList); mHasNPot = hasGlExtension("GL_OES_texture_npot"); @@ -93,9 +91,6 @@ Extensions::Extensions(): Singleton<Extensions>() { } } -Extensions::~Extensions() { -} - /////////////////////////////////////////////////////////////////////////////// // Methods /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h index 25d4c5e..731001a 100644 --- a/libs/hwui/Extensions.h +++ b/libs/hwui/Extensions.h @@ -23,6 +23,8 @@ #include <utils/SortedVector.h> #include <utils/String8.h> +#include <GLES2/gl2.h> + namespace android { namespace uirenderer { @@ -30,8 +32,10 @@ namespace uirenderer { // Classes /////////////////////////////////////////////////////////////////////////////// -class ANDROID_API Extensions: public Singleton<Extensions> { +class ANDROID_API Extensions { public: + Extensions(); + inline bool hasNPot() const { return mHasNPot; } inline bool hasFramebufferFetch() const { return mHasFramebufferFetch; } inline bool hasDiscardFramebuffer() const { return mHasDiscardFramebuffer; } @@ -55,13 +59,8 @@ public: void dump() const; private: - Extensions(); - ~Extensions(); - void findExtensions(const char* extensions, SortedVector<String8>& list) const; - friend class Singleton<Extensions>; - SortedVector<String8> mGlExtensionList; SortedVector<String8> mEglExtensionList; diff --git a/libs/hwui/Glop.h b/libs/hwui/Glop.h index bbeb19e..9150869 100644 --- a/libs/hwui/Glop.h +++ b/libs/hwui/Glop.h @@ -38,11 +38,13 @@ class Program; */ enum VertexAttribFlags { // NOTE: position attribute always enabled + kNone_Attrib = 0, kTextureCoord_Attrib = 1 << 0, kColor_Attrib = 1 << 1, kAlpha_Attrib = 1 << 2, }; + /** * Structure containing all data required to issue a single OpenGL draw * @@ -53,40 +55,53 @@ enum VertexAttribFlags { */ // TODO: PREVENT_COPY_AND_ASSIGN(...) or similar struct Glop { + struct FloatColor { + float a, r, g, b; + }; + Rect bounds; + /* + * Stores mesh - vertex and index data. + * + * buffer objects and void*s are mutually exclusive + * indices are optional + */ struct Mesh { VertexAttribFlags vertexFlags; GLuint primitiveMode; // GL_TRIANGLES and GL_TRIANGLE_STRIP supported GLuint vertexBufferObject; GLuint indexBufferObject; + const void* vertices; + const void* indices; int vertexCount; GLsizei stride; } mesh; struct Fill { Program* program; - - struct Color { - float a, r, g, b; - } color; + FloatColor color; /* TODO union shader { //... }; TODO - union filter { - //color - //matrix + vector - }; */ + ProgramDescription::ColorFilterMode filterMode; + union Filter { + struct Matrix { + float matrix[16]; + float vector[4]; + } matrix; + FloatColor color; + } filter; } fill; struct Transform { Matrix4 ortho; // TODO: out of op, since this is static per FBO Matrix4 modelView; Matrix4 canvas; - bool offset; + bool fudgingOffset; } transform; struct Blend { diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp index dafe087..e22af40 100644 --- a/libs/hwui/GlopBuilder.cpp +++ b/libs/hwui/GlopBuilder.cpp @@ -18,10 +18,12 @@ #include "Caches.h" #include "Glop.h" #include "Matrix.h" -#include "Texture.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> @@ -29,42 +31,82 @@ namespace android { namespace uirenderer { +#define TRIGGER_STAGE(stageFlag) \ + LOG_ALWAYS_FATAL_IF(stageFlag & mStageFlags, "Stage %d cannot be run twice"); \ + mStageFlags = static_cast<StageFlags>(mStageFlags | stageFlag) + GlopBuilder::GlopBuilder(RenderState& renderState, Caches& caches, Glop* outGlop) : mRenderState(renderState) , mCaches(caches) , mOutGlop(outGlop){ + mStageFlags = kInitialStage; +} + +GlopBuilder& GlopBuilder::setMeshVertexBuffer(const VertexBuffer& vertexBuffer, bool shadowInterp) { + TRIGGER_STAGE(kMeshStage); + + const VertexBuffer::MeshFeatureFlags flags = vertexBuffer.getMeshFeatureFlags(); + + bool alphaVertex = flags & VertexBuffer::kAlpha; + bool indices = flags & VertexBuffer::kIndices; + mOutGlop->mesh.vertexFlags = alphaVertex ? kAlpha_Attrib : kNone_Attrib; + mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP; + mOutGlop->mesh.vertexBufferObject = 0; + mOutGlop->mesh.vertices = vertexBuffer.getBuffer(); + mOutGlop->mesh.indexBufferObject = 0; + mOutGlop->mesh.indices = vertexBuffer.getIndices(); + mOutGlop->mesh.vertexCount = indices + ? vertexBuffer.getIndexCount() : vertexBuffer.getVertexCount(); + mOutGlop->mesh.stride = alphaVertex ? kAlphaVertexStride : kVertexStride; + + mDescription.hasVertexAlpha = alphaVertex; + mDescription.useShadowAlphaInterp = shadowInterp; + return *this; } GlopBuilder& GlopBuilder::setMeshUnitQuad() { - mOutGlop->mesh.vertexFlags = static_cast<VertexAttribFlags>(0); + TRIGGER_STAGE(kMeshStage); + + mOutGlop->mesh.vertexFlags = kNone_Attrib; mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP; mOutGlop->mesh.vertexBufferObject = mRenderState.meshState().getUnitQuadVBO(); + mOutGlop->mesh.vertices = nullptr; mOutGlop->mesh.indexBufferObject = 0; + mOutGlop->mesh.indices = nullptr; mOutGlop->mesh.vertexCount = 4; mOutGlop->mesh.stride = kTextureVertexStride; return *this; } -GlopBuilder& GlopBuilder::setTransformAndRect(ModelViewMode mode, - const Matrix4& ortho, const Matrix4& transform, - float left, float top, float right, float bottom, bool offset) { - mOutGlop->transform.ortho.load(ortho); - - mOutGlop->transform.modelView.loadTranslate(left, top, 0.0f); - if (mode == kModelViewMode_TranslateAndScale) { - mOutGlop->transform.modelView.scale(right - left, bottom - top, 1.0f); - } +GlopBuilder& GlopBuilder::setTransform(const Matrix4& ortho, + const Matrix4& transform, bool fudgingOffset) { + TRIGGER_STAGE(kTransformStage); + mOutGlop->transform.ortho.load(ortho); mOutGlop->transform.canvas.load(transform); + mOutGlop->transform.fudgingOffset = fudgingOffset; + return *this; +} - mOutGlop->transform.offset = offset; +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); + mOutGlop->bounds = destination; + return *this; +} - mOutGlop->bounds.set(left, top, right, bottom); - mOutGlop->transform.canvas.mapRect(mOutGlop->bounds); +GlopBuilder& GlopBuilder::setModelViewOffsetRect(float offsetX, float offsetY, const Rect source) { + TRIGGER_STAGE(kModelViewStage); + mOutGlop->transform.modelView.loadTranslate(offsetX, offsetY, 0.0f); + mOutGlop->bounds = source; + mOutGlop->bounds.translate(offsetX, offsetY); return *this; } GlopBuilder& GlopBuilder::setPaint(const SkPaint* paint, float alphaScale) { + TRIGGER_STAGE(kFillStage); + // TODO: support null paint const SkShader* shader = paint->getShader(); const SkColorFilter* colorFilter = paint->getColorFilter(); @@ -73,25 +115,25 @@ GlopBuilder& GlopBuilder::setPaint(const SkPaint* paint, float alphaScale) { if (mode != SkXfermode::kClear_Mode) { int color = paint->getColor(); float alpha = (SkColorGetA(color) / 255.0f) * alphaScale; - if (shader) { - // shader discards color channels - color |= 0x00FFFFFF; + if (!shader) { + float colorScale = alpha / 255.0f; + mOutGlop->fill.color = { + alpha, + colorScale * SkColorGetR(color), + colorScale * SkColorGetG(color), + colorScale * SkColorGetB(color) + }; + } else { + mOutGlop->fill.color = { alpha, 1, 1, 1 }; } - mOutGlop->fill.color = { - alpha, - alpha * SkColorGetR(color), - alpha * SkColorGetG(color), - alpha * SkColorGetB(color) - }; } else { mOutGlop->fill.color = { 1, 0, 0, 0 }; } const bool SWAP_SRC_DST = false; - const bool HAS_FRAMEBUFFER_FETCH = false; //mExtensions.hasFramebufferFetch(); - mOutGlop->blend = {GL_ZERO, GL_ZERO}; + mOutGlop->blend = { GL_ZERO, GL_ZERO }; if (mOutGlop->fill.color.a < 1.0f - || (shader && !shader->isOpaque()) + || PaintUtils::isBlendedShader(shader) || PaintUtils::isBlendedColorFilter(colorFilter) || mode != SkXfermode::kSrcOver_Mode) { if (CC_LIKELY(mode <= SkXfermode::kScreen_Mode)) { @@ -103,7 +145,7 @@ GlopBuilder& GlopBuilder::setPaint(const SkPaint* paint, float alphaScale) { // 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(HAS_FRAMEBUFFER_FETCH)) { + if (CC_UNLIKELY(mCaches.extensions().hasFramebufferFetch())) { mDescription.framebufferMode = mode; mDescription.swapSrcDst = SWAP_SRC_DST; // blending in shader, don't enable @@ -115,17 +157,58 @@ GlopBuilder& GlopBuilder::setPaint(const SkPaint* paint, float alphaScale) { } } - return *this; -} + if (shader) { + SkiaShader::describe(&mCaches, mDescription, mCaches.extensions(), *shader); + // TODO: store shader data + LOG_ALWAYS_FATAL("shaders not yet supported"); + } + + if (colorFilter) { + SkColor color; + SkXfermode::Mode mode; + SkScalar srcColorMatrix[20]; + if (colorFilter->asColorMode(&color, &mode)) { + mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::kColorBlend; + mDescription.colorMode = mode; + + const float alpha = SkColorGetA(color) / 255.0f; + float colorScale = alpha / 255.0f; + mOutGlop->fill.filter.color = { + alpha, + colorScale * SkColorGetR(color), + colorScale * SkColorGetG(color), + colorScale * SkColorGetB(color), + }; + } else if (colorFilter->asColorMatrix(srcColorMatrix)) { + mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::kColorMatrix; + + 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] = srcColorMatrix[4] / 255.0f; + colorVector[1] = srcColorMatrix[9] / 255.0f; + colorVector[2] = srcColorMatrix[14] / 255.0f; + colorVector[3] = srcColorMatrix[19] / 255.0f; + } + } else { + mOutGlop->fill.filterMode = ProgramDescription::kColorNone; + } -GlopBuilder& GlopBuilder::setTexture(Texture* texture) { - LOG_ALWAYS_FATAL("not yet supported"); return *this; } void GlopBuilder::build() { + LOG_ALWAYS_FATAL_IF(mStageFlags != kAllStages, "glop not fully prepared!"); + mDescription.modulate = mOutGlop->fill.color.a < 1.0f; mOutGlop->fill.program = mCaches.programCache.get(mDescription); + mOutGlop->transform.canvas.mapRect(mOutGlop->bounds); } } /* namespace uirenderer */ diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h index d243d76..c7464cd 100644 --- a/libs/hwui/GlopBuilder.h +++ b/libs/hwui/GlopBuilder.h @@ -27,22 +27,35 @@ namespace uirenderer { class Caches; struct Glop; +class Matrix4; class RenderState; class Texture; -class Matrix4; +class VertexBuffer; class GlopBuilder { PREVENT_COPY_AND_ASSIGN(GlopBuilder); public: GlopBuilder(RenderState& renderState, Caches& caches, Glop* outGlop); GlopBuilder& setMeshUnitQuad(); - GlopBuilder& setTransformAndRect(ModelViewMode mode, - const Matrix4& ortho, const Matrix4& transform, - float left, float top, float right, float bottom, bool offset); + GlopBuilder& setMeshVertexBuffer(const VertexBuffer& vertexBuffer, bool shadowInterp); + + GlopBuilder& setTransform(const Matrix4& ortho, const Matrix4& transform, bool fudgingOffset); + + GlopBuilder& setModelViewMapUnitToRect(const Rect destination); + GlopBuilder& setModelViewOffsetRect(float offsetX, float offsetY, const Rect source); + GlopBuilder& setPaint(const SkPaint* paint, float alphaScale); - GlopBuilder& setTexture(Texture* texture); void build(); private: + enum StageFlags { + kInitialStage = 0, + kMeshStage = 1 << 0, + kTransformStage = 1 << 1, + kModelViewStage = 1 << 2, + kFillStage = 1 << 3, + kAllStages = kMeshStage | kTransformStage | kModelViewStage | kFillStage, + } mStageFlags; + ProgramDescription mDescription; RenderState& mRenderState; Caches& mCaches; diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp index 416b0b3..fb4c785 100644 --- a/libs/hwui/GradientCache.cpp +++ b/libs/hwui/GradientCache.cpp @@ -62,9 +62,12 @@ int GradientCacheEntry::compare(const GradientCacheEntry& lhs, const GradientCac // Constructors/destructor /////////////////////////////////////////////////////////////////////////////// -GradientCache::GradientCache(): - mCache(LruCache<GradientCacheEntry, Texture*>::kUnlimitedCapacity), - mSize(0), mMaxSize(MB(DEFAULT_GRADIENT_CACHE_SIZE)) { +GradientCache::GradientCache(Extensions& extensions) + : mCache(LruCache<GradientCacheEntry, Texture*>::kUnlimitedCapacity) + , mSize(0) + , mMaxSize(MB(DEFAULT_GRADIENT_CACHE_SIZE)) + , mUseFloatTexture(extensions.hasFloatTextures()) + , mHasNpot(extensions.hasNPot()){ char property[PROPERTY_VALUE_MAX]; if (property_get(PROPERTY_GRADIENT_CACHE_SIZE, property, nullptr) > 0) { INIT_LOGD(" Setting gradient cache size to %sMB", property); @@ -76,16 +79,6 @@ GradientCache::GradientCache(): glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); mCache.setOnEntryRemovedListener(this); - - const Extensions& extensions = Extensions::getInstance(); - mUseFloatTexture = extensions.hasFloatTextures(); - mHasNpot = extensions.hasNPot(); -} - -GradientCache::GradientCache(uint32_t maxByteSize): - mCache(LruCache<GradientCacheEntry, Texture*>::kUnlimitedCapacity), - mSize(0), mMaxSize(maxByteSize) { - mCache.setOnEntryRemovedListener(this); } GradientCache::~GradientCache() { diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h index 1714e6d..08319ea 100644 --- a/libs/hwui/GradientCache.h +++ b/libs/hwui/GradientCache.h @@ -104,8 +104,7 @@ inline hash_t hash_type(const GradientCacheEntry& entry) { */ class GradientCache: public OnEntryRemoved<GradientCacheEntry, Texture*> { public: - GradientCache(); - GradientCache(uint32_t maxByteSize); + GradientCache(Extensions& extensions); ~GradientCache(); /** diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp index d2f9a94..b4b14e8 100644 --- a/libs/hwui/LayerRenderer.cpp +++ b/libs/hwui/LayerRenderer.cpp @@ -337,7 +337,7 @@ void LayerRenderer::flushLayer(RenderState& renderState, Layer* layer) { if (fbo) { // If possible, discard any enqueud operations on deferred // rendering architectures - if (Extensions::getInstance().hasDiscardFramebuffer()) { + if (Caches::getInstance().extensions().hasDiscardFramebuffer()) { GLuint previousFbo = renderState.getFramebuffer(); if (fbo != previousFbo) { renderState.bindFramebuffer(fbo); diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index b10aea3..c4622f6 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -89,7 +89,6 @@ static inline T min(T a, T b) { OpenGLRenderer::OpenGLRenderer(RenderState& renderState) : mState(*this) , mCaches(Caches::getInstance()) - , mExtensions(Extensions::getInstance()) , mRenderState(renderState) , mFrameStarted(false) , mScissorOptimizationDisabled(false) @@ -195,7 +194,7 @@ void OpenGLRenderer::discardFramebuffer(float left, float top, float right, floa // If we know that we are going to redraw the entire framebuffer, // perform a discard to let the driver know we don't need to preserve // the back buffer for this frame. - if (mExtensions.hasDiscardFramebuffer() && + if (mCaches.extensions().hasDiscardFramebuffer() && left <= 0.0f && top <= 0.0f && right >= mState.getWidth() && bottom >= mState.getHeight()) { const bool isFbo = onGetTargetFbo() == 0; const GLenum attachments[] = { @@ -905,7 +904,7 @@ void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) { setupDrawTextureTransformUniforms(layer->getTexTransform()); setupDrawMesh(&mMeshVertices[0].x, &mMeshVertices[0].u); - glDrawArrays(GL_TRIANGLE_STRIP, 0, kMeshCount); + glDrawArrays(GL_TRIANGLE_STRIP, 0, kUnitQuadCount); } void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) { @@ -947,7 +946,7 @@ void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) drawTextureMesh(x, y, x + rect.getWidth(), y + rect.getHeight(), layer->getTexture(), &layerPaint, blend, &mMeshVertices[0].x, &mMeshVertices[0].u, - GL_TRIANGLE_STRIP, kMeshCount, swap, swap || simpleTransform); + GL_TRIANGLE_STRIP, kUnitQuadCount, swap, swap || simpleTransform); resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f); } @@ -1387,7 +1386,8 @@ void OpenGLRenderer::attachStencilBufferToLayer(Layer* layer) { endTiling(); RenderBuffer* buffer = mCaches.renderBufferCache.get( - Stencil::getSmallestStencilFormat(), layer->getWidth(), layer->getHeight()); + Stencil::getSmallestStencilFormat(), + layer->getWidth(), layer->getHeight()); layer->setStencilRenderBuffer(buffer); startTiling(layer->clipRect, layer->layer.getHeight()); @@ -1573,6 +1573,18 @@ void OpenGLRenderer::debugClip() { #endif } +void OpenGLRenderer::renderGlop(const Glop& glop) { + if (mState.getDirtyClip()) { + if (mRenderState.scissor().isEnabled()) { + setScissorFromClip(); + } + + setStencilFromClip(); + } + mRenderState.render(glop); + dirtyLayer(glop.bounds.left, glop.bounds.top, glop.bounds.right, glop.bounds.bottom); +} + /////////////////////////////////////////////////////////////////////////////// // Drawing commands /////////////////////////////////////////////////////////////////////////////// @@ -1601,9 +1613,9 @@ void OpenGLRenderer::setupDraw(bool clearLayer) { // 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 = !mCaches.debugOverdraw && - mCaches.debugStencilClip == Caches::kStencilShowHighlight && - mRenderState.stencil().isTestEnabled(); + mDescription.hasDebugHighlight = !mCaches.debugOverdraw + && mCaches.debugStencilClip == Caches::kStencilShowHighlight + && mRenderState.stencil().isTestEnabled(); } void OpenGLRenderer::setupDrawWithTexture(bool isAlpha8) { @@ -1663,7 +1675,7 @@ void OpenGLRenderer::setupDrawColor(float r, float g, float b, float a) { void OpenGLRenderer::setupDrawShader(const SkShader* shader) { if (shader != nullptr) { - SkiaShader::describe(&mCaches, mDescription, mExtensions, *shader); + SkiaShader::describe(&mCaches, mDescription, mCaches.extensions(), *shader); } } @@ -1784,7 +1796,8 @@ void OpenGLRenderer::setupDrawShaderUniforms(const SkShader* shader, bool ignore mModelViewMatrix.load(modelViewWithoutTransform); } - SkiaShader::setupProgram(&mCaches, mModelViewMatrix, &mTextureUnit, mExtensions, *shader); + SkiaShader::setupProgram(&mCaches, mModelViewMatrix, &mTextureUnit, + mCaches.extensions(), *shader); } void OpenGLRenderer::setupDrawColorFilterUniforms(const SkColorFilter* filter) { @@ -1978,7 +1991,7 @@ void OpenGLRenderer::drawAlphaBitmap(Texture* texture, float left, float top, // bitmaps get packed in the atlas drawAlpha8TextureMesh(x, y, x + texture->width, y + texture->height, texture->id, paint, (GLvoid*) nullptr, (GLvoid*) kMeshTextureOffset, - GL_TRIANGLE_STRIP, kMeshCount, ignoreTransform); + GL_TRIANGLE_STRIP, kUnitQuadCount, ignoreTransform); } /** @@ -2132,7 +2145,7 @@ void OpenGLRenderer::drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int m setupDrawBlending(paint, true); setupDrawProgram(); setupDrawDirtyRegionsDisabled(); - setupDrawModelView(kModelViewMode_TranslateAndScale, false, 0.0f, 0.0f, 1.0f, 1.0f); + setupDrawModelView(kModelViewMode_Translate, false, 0, 0, 0, 0); setupDrawTexture(texture->id); setupDrawPureColorUniforms(); setupDrawColorFilterUniforms(getColorFilter(paint)); @@ -2218,12 +2231,12 @@ void OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, drawAlpha8TextureMesh(dstLeft, dstTop, dstRight, dstBottom, texture->id, paint, &mMeshVertices[0].x, &mMeshVertices[0].u, - GL_TRIANGLE_STRIP, kMeshCount, ignoreTransform); + GL_TRIANGLE_STRIP, kUnitQuadCount, ignoreTransform); } else { drawTextureMesh(dstLeft, dstTop, dstRight, dstBottom, texture->id, paint, texture->blend, &mMeshVertices[0].x, &mMeshVertices[0].u, - GL_TRIANGLE_STRIP, kMeshCount, false, ignoreTransform); + GL_TRIANGLE_STRIP, kUnitQuadCount, false, ignoreTransform); } if (CC_UNLIKELY(useScaleTransform)) { @@ -2333,17 +2346,33 @@ void OpenGLRenderer::drawVertexBuffer(float translateX, float translateY, return; } + if (!paint->getShader() && !currentSnapshot()->roundRectClipState) { + Glop glop; + GlopBuilder aBuilder(mRenderState, mCaches, &glop); + bool fudgeOffset = displayFlags & kVertexBuffer_Offset; + bool shadowInterp = displayFlags & kVertexBuffer_ShadowInterp; + aBuilder.setMeshVertexBuffer(vertexBuffer, shadowInterp) + .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), fudgeOffset) + .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds()) + .setPaint(paint, currentSnapshot()->alpha) + .build(); + renderGlop(glop); + return; + } + + + const VertexBuffer::MeshFeatureFlags meshFeatureFlags = vertexBuffer.getMeshFeatureFlags(); Rect bounds(vertexBuffer.getBounds()); bounds.translate(translateX, translateY); dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, *currentTransform()); int color = paint->getColor(); - bool isAA = paint->isAntiAlias(); + bool isAA = meshFeatureFlags & VertexBuffer::kAlpha; setupDraw(); setupDrawNoTexture(); if (isAA) setupDrawVertexAlpha((displayFlags & kVertexBuffer_ShadowInterp)); - setupDrawColor(color, ((color >> 24) & 0xFF) * writableSnapshot()->alpha); + setupDrawColor(color, ((color >> 24) & 0xFF) * currentSnapshot()->alpha); setupDrawColorFilter(getColorFilter(paint)); setupDrawShader(getShader(paint)); setupDrawBlending(paint, isAA); @@ -2369,22 +2398,13 @@ void OpenGLRenderer::drawVertexBuffer(float translateX, float translateY, glVertexAttribPointer(alphaSlot, 1, GL_FLOAT, GL_FALSE, kAlphaVertexStride, alphaCoords); } - const VertexBuffer::Mode mode = vertexBuffer.getMode(); - if (mode == VertexBuffer::kStandard) { - mRenderState.meshState().unbindIndicesBuffer(); - glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexBuffer.getVertexCount()); - } else if (mode == VertexBuffer::kOnePolyRingShadow) { - mRenderState.meshState().bindShadowIndicesBuffer(); - glDrawElements(GL_TRIANGLE_STRIP, ONE_POLY_RING_SHADOW_INDEX_COUNT, - GL_UNSIGNED_SHORT, nullptr); - } else if (mode == VertexBuffer::kTwoPolyRingShadow) { - mRenderState.meshState().bindShadowIndicesBuffer(); - glDrawElements(GL_TRIANGLE_STRIP, TWO_POLY_RING_SHADOW_INDEX_COUNT, - GL_UNSIGNED_SHORT, nullptr); - } else if (mode == VertexBuffer::kIndices) { + if (meshFeatureFlags & VertexBuffer::kIndices) { mRenderState.meshState().unbindIndicesBuffer(); glDrawElements(GL_TRIANGLE_STRIP, vertexBuffer.getIndexCount(), GL_UNSIGNED_SHORT, vertexBuffer.getIndices()); + } else { + mRenderState.meshState().unbindIndicesBuffer(); + glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexBuffer.getVertexCount()); } if (isAA) { @@ -2666,7 +2686,7 @@ void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text, setupDrawShaderUniforms(getShader(paint)); setupDrawMesh(nullptr, (GLvoid*) kMeshTextureOffset); - glDrawArrays(GL_TRIANGLE_STRIP, 0, kMeshCount); + glDrawArrays(GL_TRIANGLE_STRIP, 0, kUnitQuadCount); } bool OpenGLRenderer::canSkipText(const SkPaint* paint) const { @@ -3100,7 +3120,7 @@ void OpenGLRenderer::drawPathTexture(const PathTexture* texture, setupDrawShaderUniforms(getShader(paint)); setupDrawMesh(nullptr, (GLvoid*) kMeshTextureOffset); - glDrawArrays(GL_TRIANGLE_STRIP, 0, kMeshCount); + glDrawArrays(GL_TRIANGLE_STRIP, 0, kUnitQuadCount); } // Same values used by Skia @@ -3259,6 +3279,20 @@ void OpenGLRenderer::drawColorRects(const float* rects, int count, const SkPaint void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom, const SkPaint* paint, bool ignoreTransform) { + + if (!paint->getShader() && !currentSnapshot()->roundRectClipState) { + const Matrix4& transform = ignoreTransform ? Matrix4::identity() : *currentTransform(); + Glop glop; + GlopBuilder aBuilder(mRenderState, mCaches, &glop); + aBuilder.setMeshUnitQuad() + .setTransform(currentSnapshot()->getOrthoMatrix(), transform, false) + .setModelViewMapUnitToRect(Rect(left, top, right, bottom)) + .setPaint(paint, currentSnapshot()->alpha) + .build(); + renderGlop(glop); + return; + } + int color = paint->getColor(); // If a shader is set, preserve only the alpha if (getShader(paint)) { @@ -3279,7 +3313,7 @@ void OpenGLRenderer::drawColorRect(float left, float top, float right, float bot setupDrawColorFilterUniforms(getColorFilter(paint)); setupDrawSimpleMesh(); - glDrawArrays(GL_TRIANGLE_STRIP, 0, kMeshCount); + glDrawArrays(GL_TRIANGLE_STRIP, 0, kUnitQuadCount); } void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom, @@ -3306,11 +3340,11 @@ void OpenGLRenderer::drawTextureRect(float left, float top, float right, float b texture->setFilter(GL_NEAREST, true); drawTextureMesh(x, y, x + texture->width, y + texture->height, texture->id, paint, texture->blend, vertices, texCoords, - GL_TRIANGLE_STRIP, kMeshCount, false, true); + GL_TRIANGLE_STRIP, kUnitQuadCount, false, true); } else { texture->setFilter(getFilter(paint), true); drawTextureMesh(left, top, right, bottom, texture->id, paint, - texture->blend, vertices, texCoords, GL_TRIANGLE_STRIP, kMeshCount); + texture->blend, vertices, texCoords, GL_TRIANGLE_STRIP, kUnitQuadCount); } if (texture->uvMapper) { @@ -3405,7 +3439,7 @@ void OpenGLRenderer::drawAlpha8TextureMesh(float left, float top, float right, f void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode, ProgramDescription& description, bool swapSrcDst) { - if (writableSnapshot()->roundRectClipState != nullptr /*&& !mSkipOutlineClip*/) { + if (currentSnapshot()->roundRectClipState != nullptr /*&& !mSkipOutlineClip*/) { blend = true; mDescription.hasRoundRectClip = true; } @@ -3420,7 +3454,7 @@ void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode, // If the blend mode cannot be implemented using shaders, fall // back to the default SrcOver blend mode instead if (CC_UNLIKELY(mode > SkXfermode::kScreen_Mode)) { - if (CC_UNLIKELY(mExtensions.hasFramebufferFetch())) { + if (CC_UNLIKELY(mCaches.extensions().hasFramebufferFetch())) { description.framebufferMode = mode; description.swapSrcDst = swapSrcDst; diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index f0de089..f097041 100755 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -57,6 +57,7 @@ namespace android { namespace uirenderer { class DeferredDisplayState; +struct Glop; class RenderState; class RenderNode; class TextSetupFunctor; @@ -529,10 +530,11 @@ protected: CanvasState mState; Caches& mCaches; - Extensions& mExtensions; // TODO: move to RenderState RenderState& mRenderState; private: + void renderGlop(const Glop& glop); + /** * Discards the content of the framebuffer if supported by the driver. * This method should be called at the beginning of a frame to optimize diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp index ceec4fc..3d8a749 100644 --- a/libs/hwui/PathTessellator.cpp +++ b/libs/hwui/PathTessellator.cpp @@ -783,6 +783,7 @@ void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint, Rect bounds(path.getBounds()); paintInfo.expandBoundsForStroke(&bounds); vertexBuffer.setBounds(bounds); + vertexBuffer.setMeshFeatureFlags(paintInfo.isAA ? VertexBuffer::kAlpha : VertexBuffer::kNone); } template <class TYPE> @@ -840,6 +841,7 @@ void PathTessellator::tessellatePoints(const float* points, int count, const SkP // expand bounds from vertex coords to pixel data paintInfo.expandBoundsForStroke(&bounds); vertexBuffer.setBounds(bounds); + vertexBuffer.setMeshFeatureFlags(paintInfo.isAA ? VertexBuffer::kAlpha : VertexBuffer::kNone); } void PathTessellator::tessellateLines(const float* points, int count, const SkPaint* paint, @@ -890,6 +892,7 @@ void PathTessellator::tessellateLines(const float* points, int count, const SkPa // expand bounds from vertex coords to pixel data paintInfo.expandBoundsForStroke(&bounds); vertexBuffer.setBounds(bounds); + vertexBuffer.setMeshFeatureFlags(paintInfo.isAA ? VertexBuffer::kAlpha : VertexBuffer::kNone); } /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h index b637450..01231b5 100644 --- a/libs/hwui/Program.h +++ b/libs/hwui/Program.h @@ -102,7 +102,7 @@ typedef uint64_t programid; * A ProgramDescription must be used in conjunction with a ProgramCache. */ struct ProgramDescription { - enum ColorModifier { + enum ColorFilterMode { kColorNone = 0, kColorMatrix, kColorBlend @@ -148,7 +148,7 @@ struct ProgramDescription { GLenum bitmapWrapT; // Color operations - ColorModifier colorOp; + ColorFilterMode colorOp; SkXfermode::Mode colorMode; // Framebuffer blending (requires Extensions.hasFramebufferFetch()) diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp index 3bbd520..8c6a91cc 100644 --- a/libs/hwui/ProgramCache.cpp +++ b/libs/hwui/ProgramCache.cpp @@ -404,7 +404,8 @@ const char* gBlendOps[18] = { // Constructors/destructors /////////////////////////////////////////////////////////////////////////////// -ProgramCache::ProgramCache(): mHasES3(Extensions::getInstance().getMajorGlVersion() >= 3) { +ProgramCache::ProgramCache(Extensions& extensions) + : mHasES3(extensions.getMajorGlVersion() >= 3) { } ProgramCache::~ProgramCache() { diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h index 30fa0df..1dadda1 100644 --- a/libs/hwui/ProgramCache.h +++ b/libs/hwui/ProgramCache.h @@ -40,7 +40,7 @@ namespace uirenderer { */ class ProgramCache { public: - ProgramCache(); + ProgramCache(Extensions& extensions); ~ProgramCache(); Program* get(const ProgramDescription& description); diff --git a/libs/hwui/Query.h b/libs/hwui/Query.h deleted file mode 100644 index e25b16b..0000000 --- a/libs/hwui/Query.h +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2013 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. - */ - -#ifndef ANDROID_HWUI_QUERY_H -#define ANDROID_HWUI_QUERY_H - -#include <GLES3/gl3.h> - -#include "Extensions.h" - -namespace android { -namespace uirenderer { - -/** - * A Query instance can be used to perform occlusion queries. If the device - * does not support occlusion queries, the result of a query will always be - * 0 and the result will always be marked available. - * - * To run an occlusion query successfully, you must start end end the query: - * - * Query query; - * query.begin(); - * // execute OpenGL calls - * query.end(); - * GLuint result = query.getResult(); - */ -class Query { -public: - /** - * Possible query targets. - */ - enum Target { - /** - * Indicates if any sample passed the depth & stencil tests. - */ - kTargetSamples = GL_ANY_SAMPLES_PASSED, - /** - * Indicates if any sample passed the depth & stencil tests. - * The implementation may choose to use a less precise version - * of the test, potentially resulting in false positives. - */ - kTargetConservativeSamples = GL_ANY_SAMPLES_PASSED_CONSERVATIVE, - }; - - /** - * Creates a new query with the specified target. The default - * target is kTargetSamples (of GL_ANY_SAMPLES_PASSED in OpenGL.) - */ - Query(Target target = kTargetSamples): mActive(false), mTarget(target), - mCanQuery(Extensions::getInstance().hasOcclusionQueries()), - mQuery(0) { - } - - ~Query() { - if (mQuery) { - glDeleteQueries(1, &mQuery); - } - } - - /** - * Begins the query. If the query has already begun or if the device - * does not support occlusion queries, calling this method as no effect. - * After calling this method successfully, the query is marked active. - */ - void begin() { - if (!mActive && mCanQuery) { - if (!mQuery) { - glGenQueries(1, &mQuery); - } - - glBeginQuery(mTarget, mQuery); - mActive = true; - } - } - - /** - * Ends the query. If the query has already begun or if the device - * does not support occlusion queries, calling this method as no effect. - * After calling this method successfully, the query is marked inactive. - */ - void end() { - if (mQuery && mActive) { - glEndQuery(mTarget); - mActive = false; - } - } - - /** - * Returns true if the query is active, false otherwise. - */ - bool isActive() { - return mActive; - } - - /** - * Returns true if the result of the query is available, - * false otherwise. Calling getResult() before the result - * is available may result in the calling thread being blocked. - * If the device does not support queries, this method always - * returns true. - */ - bool isResultAvailable() { - if (!mQuery) return true; - - GLuint result; - glGetQueryObjectuiv(mQuery, GL_QUERY_RESULT_AVAILABLE, &result); - return result == GL_TRUE; - } - - /** - * Returns the result of the query. If the device does not - * support queries this method will return 0. - * - * Calling this method implicitely calls end() if the query - * is currently active. - */ - GLuint getResult() { - if (!mQuery) return 0; - - end(); - - GLuint result; - glGetQueryObjectuiv(mQuery, GL_QUERY_RESULT, &result); - return result; - } - - -private: - bool mActive; - GLenum mTarget; - bool mCanQuery; - GLuint mQuery; - -}; // class Query - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_QUERY_H diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp index f8917e3..ca7f48d 100644 --- a/libs/hwui/ShadowTessellator.cpp +++ b/libs/hwui/ShadowTessellator.cpp @@ -113,33 +113,6 @@ void ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque, #endif } -void ShadowTessellator::generateShadowIndices(uint16_t* shadowIndices) { - int currentIndex = 0; - const int rays = SHADOW_RAY_COUNT; - // For the penumbra area. - for (int layer = 0; layer < 2; layer ++) { - int baseIndex = layer * rays; - for (int i = 0; i < rays; i++) { - shadowIndices[currentIndex++] = i + baseIndex; - shadowIndices[currentIndex++] = rays + i + baseIndex; - } - // To close the loop, back to the ray 0. - shadowIndices[currentIndex++] = 0 + baseIndex; - // Note this is the same as the first index of next layer loop. - shadowIndices[currentIndex++] = rays + baseIndex; - } - -#if DEBUG_SHADOW - if (currentIndex != MAX_SHADOW_INDEX_COUNT) { - ALOGW("vertex index count is wrong. current %d, expected %d", - currentIndex, MAX_SHADOW_INDEX_COUNT); - } - for (int i = 0; i < MAX_SHADOW_INDEX_COUNT; i++) { - ALOGD("vertex index is (%d, %d)", i, shadowIndices[i]); - } -#endif -} - /** * Calculate the centroid of a 2d polygon. * diff --git a/libs/hwui/ShadowTessellator.h b/libs/hwui/ShadowTessellator.h index 16ad91d..c04d8ef 100644 --- a/libs/hwui/ShadowTessellator.h +++ b/libs/hwui/ShadowTessellator.h @@ -78,8 +78,6 @@ public: const mat4& receiverTransform, const Vector3& lightCenter, int lightRadius, const Rect& casterBounds, const Rect& localClip, VertexBuffer& shadowVertexBuffer); - static void generateShadowIndices(uint16_t* shadowIndices); - static Vector2 centroid2d(const Vector2* poly, int polyLength); static bool isClockwise(const Vector2* polygon, int len); diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp index a03192a..b3b06d6 100644 --- a/libs/hwui/SpotShadow.cpp +++ b/libs/hwui/SpotShadow.cpp @@ -1031,7 +1031,7 @@ void SpotShadow::generateTriangleStrip(bool isCasterOpaque, float shadowStrength ShadowTessellator::checkOverflow(vertexBufferIndex, totalVertexCount, "Spot Vertex Buffer"); ShadowTessellator::checkOverflow(indexBufferIndex, totalIndexCount, "Spot Index Buffer"); - shadowTriangleStrip.setMode(VertexBuffer::kIndices); + shadowTriangleStrip.setMeshFeatureFlags(VertexBuffer::kAlpha | VertexBuffer::kIndices); shadowTriangleStrip.computeBounds<AlphaVertex>(); } diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp index fe8fb5b..f4f8e44 100644 --- a/libs/hwui/TextureCache.cpp +++ b/libs/hwui/TextureCache.cpp @@ -38,10 +38,12 @@ namespace uirenderer { // Constructors/destructor /////////////////////////////////////////////////////////////////////////////// -TextureCache::TextureCache(): - mCache(LruCache<uint32_t, Texture*>::kUnlimitedCapacity), - mSize(0), mMaxSize(MB(DEFAULT_TEXTURE_CACHE_SIZE)), - mFlushRate(DEFAULT_TEXTURE_CACHE_FLUSH_RATE), mAssetAtlas(nullptr) { +TextureCache::TextureCache() + : mCache(LruCache<uint32_t, Texture*>::kUnlimitedCapacity) + , mSize(0) + , mMaxSize(MB(DEFAULT_TEXTURE_CACHE_SIZE)) + , mFlushRate(DEFAULT_TEXTURE_CACHE_FLUSH_RATE) + , mAssetAtlas(nullptr) { char property[PROPERTY_VALUE_MAX]; if (property_get(PROPERTY_TEXTURE_CACHE_SIZE, property, nullptr) > 0) { INIT_LOGD(" Setting texture cache size to %sMB", property); @@ -59,20 +61,6 @@ TextureCache::TextureCache(): DEFAULT_TEXTURE_CACHE_FLUSH_RATE * 100.0f); } - init(); -} - -TextureCache::TextureCache(uint32_t maxByteSize): - mCache(LruCache<uint32_t, Texture*>::kUnlimitedCapacity), - mSize(0), mMaxSize(maxByteSize), mAssetAtlas(nullptr) { - init(); -} - -TextureCache::~TextureCache() { - mCache.clear(); -} - -void TextureCache::init() { mCache.setOnEntryRemovedListener(this); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); @@ -81,6 +69,10 @@ void TextureCache::init() { mDebugEnabled = readDebugLevel() & kDebugCaches; } +TextureCache::~TextureCache() { + mCache.clear(); +} + /////////////////////////////////////////////////////////////////////////////// // Size management /////////////////////////////////////////////////////////////////////////////// @@ -280,7 +272,7 @@ void TextureCache::generateTexture(const SkBitmap* bitmap, Texture* texture, boo // We could also enable mipmapping if both bitmap dimensions are powers // of 2 but we'd have to deal with size changes. Let's keep this simple - const bool canMipMap = Extensions::getInstance().hasNPot(); + const bool canMipMap = Caches::getInstance().extensions().hasNPot(); // If the texture had mipmap enabled but not anymore, // force a glTexImage2D to discard the mipmap levels @@ -355,7 +347,8 @@ void TextureCache::uploadLoFiTexture(bool resize, const SkBitmap* bitmap, void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei stride, GLsizei bpp, GLsizei width, GLsizei height, GLenum type, const GLvoid * data) { glPixelStorei(GL_UNPACK_ALIGNMENT, bpp); - const bool useStride = stride != width && Extensions::getInstance().hasUnpackRowLength(); + const bool useStride = stride != width + && Caches::getInstance().extensions().hasUnpackRowLength(); if ((stride == width) || useStride) { if (useStride) { glPixelStorei(GL_UNPACK_ROW_LENGTH, stride); diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h index b97d92d..a2c6380 100644 --- a/libs/hwui/TextureCache.h +++ b/libs/hwui/TextureCache.h @@ -52,10 +52,9 @@ class AssetAtlas; * Any texture added to the cache causing the cache to grow beyond the maximum * allowed size will also cause the oldest texture to be kicked out. */ -class TextureCache: public OnEntryRemoved<uint32_t, Texture*> { +class TextureCache : public OnEntryRemoved<uint32_t, Texture*> { public: TextureCache(); - TextureCache(uint32_t maxByteSize); ~TextureCache(); /** @@ -146,8 +145,6 @@ private: void uploadToTexture(bool resize, GLenum format, GLsizei stride, GLsizei bpp, GLsizei width, GLsizei height, GLenum type, const GLvoid * data); - void init(); - LruCache<uint32_t, Texture*> mCache; uint32_t mSize; diff --git a/libs/hwui/VertexBuffer.h b/libs/hwui/VertexBuffer.h index d81dd42..9be4d84 100644 --- a/libs/hwui/VertexBuffer.h +++ b/libs/hwui/VertexBuffer.h @@ -24,11 +24,10 @@ namespace uirenderer { class VertexBuffer { public: - enum Mode { - kStandard = 0, - kOnePolyRingShadow = 1, - kTwoPolyRingShadow = 2, - kIndices = 3 + enum MeshFeatureFlags { + kNone = 0, + kAlpha = 1 << 0, + kIndices = 1 << 1, }; VertexBuffer() @@ -39,7 +38,7 @@ public: , mAllocatedVertexCount(0) , mAllocatedIndexCount(0) , mByteCount(0) - , mMode(kStandard) + , mMeshFeatureFlags(kNone) , mReallocBuffer(nullptr) , mCleanupMethod(nullptr) , mCleanupIndexMethod(nullptr) @@ -135,10 +134,12 @@ public: void updateVertexCount(unsigned int newCount) { mVertexCount = MathUtils::min(newCount, mAllocatedVertexCount); } - Mode getMode() const { return mMode; } + MeshFeatureFlags getMeshFeatureFlags() const { return mMeshFeatureFlags; } + void setMeshFeatureFlags(int flags) { + mMeshFeatureFlags = static_cast<MeshFeatureFlags>(flags); + } void setBounds(Rect bounds) { mBounds = bounds; } - void setMode(Mode mode) { mMode = mode; } template <class TYPE> void createDegenerateSeparators(int allocSize) { @@ -166,7 +167,7 @@ private: unsigned int mAllocatedIndexCount; unsigned int mByteCount; - Mode mMode; + MeshFeatureFlags mMeshFeatureFlags; void* mReallocBuffer; // used for multi-allocation diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp index 53fa0dc..9314126 100644 --- a/libs/hwui/font/CacheTexture.cpp +++ b/libs/hwui/font/CacheTexture.cpp @@ -120,7 +120,7 @@ CacheTexture::CacheTexture(uint16_t width, uint16_t height, GLenum format, uint3 // OpenGL ES 3.0+ lets us specify the row length for unpack operations such // as glTexSubImage2D(). This allows us to upload a sub-rectangle of a texture. // With OpenGL ES 2.0 we have to upload entire stripes instead. - mHasUnpackRowLength = Extensions::getInstance().hasUnpackRowLength(); + mHasUnpackRowLength = mCaches.extensions().hasUnpackRowLength(); } CacheTexture::~CacheTexture() { diff --git a/libs/hwui/renderstate/Blend.cpp b/libs/hwui/renderstate/Blend.cpp index 93088e4..c751dba 100644 --- a/libs/hwui/renderstate/Blend.cpp +++ b/libs/hwui/renderstate/Blend.cpp @@ -127,6 +127,10 @@ void Blend::setFactors(GLenum srcMode, GLenum dstMode) { } } +void Blend::dump() { + ALOGD("Blend: enabled %d, func src %d, dst %d", mEnabled, mSrcMode, mDstMode); +} + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderstate/Blend.h b/libs/hwui/renderstate/Blend.h index 31d7dde..6d0c115 100644 --- a/libs/hwui/renderstate/Blend.h +++ b/libs/hwui/renderstate/Blend.h @@ -35,6 +35,8 @@ public: static void getFactors(SkXfermode::Mode mode, bool swapSrcDst, GLenum* outSrc, GLenum* outDst); void setFactors(GLenum src, GLenum dst); + + void dump(); private: Blend(); void invalidate(); diff --git a/libs/hwui/renderstate/MeshState.cpp b/libs/hwui/renderstate/MeshState.cpp index 50c09c8..ce6030d 100644 --- a/libs/hwui/renderstate/MeshState.cpp +++ b/libs/hwui/renderstate/MeshState.cpp @@ -23,22 +23,20 @@ namespace android { namespace uirenderer { MeshState::MeshState() - : mCurrentPositionPointer(this) + : mCurrentIndicesBuffer(0) + , mCurrentPixelBuffer(0) + , mCurrentPositionPointer(this) , mCurrentPositionStride(0) , mCurrentTexCoordsPointer(this) , mCurrentTexCoordsStride(0) - , mTexCoordsArrayEnabled(false) { + , mTexCoordsArrayEnabled(false) + , mQuadListIndices(0) { glGenBuffers(1, &mUnitQuadBuffer); glBindBuffer(GL_ARRAY_BUFFER, mUnitQuadBuffer); glBufferData(GL_ARRAY_BUFFER, sizeof(kUnitQuadVertices), kUnitQuadVertices, GL_STATIC_DRAW); mCurrentBuffer = mUnitQuadBuffer; - mCurrentIndicesBuffer = 0; - mCurrentPixelBuffer = 0; - - mQuadListIndices = 0; - mShadowStripsIndices = 0; // position attribute always enabled glEnableVertexAttribArray(Program::kBindingPosition); @@ -50,9 +48,10 @@ MeshState::~MeshState() { glDeleteBuffers(1, &mQuadListIndices); mQuadListIndices = 0; +} - glDeleteBuffers(1, &mShadowStripsIndices); - mShadowStripsIndices = 0; +void MeshState::dump() { + ALOGD("MeshState vertices: unitQuad %d, current %d", mUnitQuadBuffer, mCurrentBuffer); } /////////////////////////////////////////////////////////////////////////////// @@ -65,18 +64,17 @@ bool MeshState::bindMeshBuffer() { bool MeshState::bindMeshBuffer(GLuint buffer) { if (!buffer) buffer = mUnitQuadBuffer; - if (mCurrentBuffer != buffer) { - glBindBuffer(GL_ARRAY_BUFFER, buffer); - mCurrentBuffer = buffer; - return true; - } - return false; + return bindMeshBufferInternal(buffer); } bool MeshState::unbindMeshBuffer() { - if (mCurrentBuffer) { - glBindBuffer(GL_ARRAY_BUFFER, 0); - mCurrentBuffer = 0; + return bindMeshBufferInternal(0); +} + +bool MeshState::bindMeshBufferInternal(GLuint buffer) { + if (mCurrentBuffer != buffer) { + glBindBuffer(GL_ARRAY_BUFFER, buffer); + mCurrentBuffer = buffer; return true; } return false; @@ -163,20 +161,6 @@ bool MeshState::bindQuadIndicesBuffer() { return bindIndicesBufferInternal(mQuadListIndices); } -bool MeshState::bindShadowIndicesBuffer() { - if (!mShadowStripsIndices) { - std::unique_ptr<uint16_t[]> shadowIndices(new uint16_t[MAX_SHADOW_INDEX_COUNT]); - ShadowTessellator::generateShadowIndices(shadowIndices.get()); - glGenBuffers(1, &mShadowStripsIndices); - bool force = bindIndicesBufferInternal(mShadowStripsIndices); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_SHADOW_INDEX_COUNT * sizeof(uint16_t), - shadowIndices.get(), GL_STATIC_DRAW); - return force; - } - - return bindIndicesBufferInternal(mShadowStripsIndices); -} - bool MeshState::unbindIndicesBuffer() { if (mCurrentIndicesBuffer) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); diff --git a/libs/hwui/renderstate/MeshState.h b/libs/hwui/renderstate/MeshState.h index 5cb1143..afc6267 100644 --- a/libs/hwui/renderstate/MeshState.h +++ b/libs/hwui/renderstate/MeshState.h @@ -47,7 +47,7 @@ const GLsizei kMeshTextureOffset = 2 * sizeof(float); const GLsizei kVertexAlphaOffset = 2 * sizeof(float); const GLsizei kVertexAAWidthOffset = 2 * sizeof(float); const GLsizei kVertexAALengthOffset = 3 * sizeof(float); -const GLsizei kMeshCount = 4; +const GLsizei kUnitQuadCount = 4; class MeshState { private: @@ -55,6 +55,7 @@ private: public: ~MeshState(); + void dump(); /////////////////////////////////////////////////////////////////////////////// // Buffer objects /////////////////////////////////////////////////////////////////////////////// @@ -107,7 +108,6 @@ public: * gMaxNumberOfQuads quads. */ bool bindQuadIndicesBuffer(); - bool bindShadowIndicesBuffer(); bool unbindIndicesBuffer(); /////////////////////////////////////////////////////////////////////////////// @@ -116,9 +116,9 @@ public: GLuint getUnitQuadVBO() { return mUnitQuadBuffer; } private: MeshState(); + bool bindMeshBufferInternal(const GLuint buffer); bool bindIndicesBufferInternal(const GLuint buffer); - // VBO to draw with GLuint mUnitQuadBuffer; GLuint mCurrentBuffer; @@ -134,7 +134,6 @@ private: // Global index buffer GLuint mQuadListIndices; - GLuint mShadowStripsIndices; }; } /* namespace uirenderer */ diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp index d3f6277..a819d9a 100644 --- a/libs/hwui/renderstate/RenderState.cpp +++ b/libs/hwui/renderstate/RenderState.cpp @@ -17,6 +17,7 @@ #include "renderthread/CanvasContext.h" #include "renderthread/EglManager.h" +#include "utils/GLUtils.h" namespace android { namespace uirenderer { @@ -213,18 +214,41 @@ void RenderState::render(const Glop& glop) { const Glop::Mesh& mesh = glop.mesh; const Glop::Fill& shader = glop.fill; + // -------------------------------------------- // ---------- Shader + uniform setup ---------- + // -------------------------------------------- mCaches->setProgram(shader.program); - Glop::Fill::Color color = shader.color; + Glop::FloatColor color = shader.color; shader.program->setColor(color.r, color.g, color.b, color.a); shader.program->set(glop.transform.ortho, glop.transform.modelView, glop.transform.canvas, - glop.transform.offset); + glop.transform.fudgingOffset); + + if (glop.fill.filterMode == ProgramDescription::kColorBlend) { + const Glop::FloatColor& color = glop.fill.filter.color; + glUniform4f(mCaches->program().getUniform("colorBlend"), + color.r, color.g, color.b, color.a); + } else if (glop.fill.filterMode == ProgramDescription::kColorMatrix) { + glUniformMatrix4fv(mCaches->program().getUniform("colorMatrix"), 1, GL_FALSE, + glop.fill.filter.matrix.matrix); + glUniform4fv(mCaches->program().getUniform("colorMatrixVector"), 1, + glop.fill.filter.matrix.vector); + } + // -------------------------------- // ---------- Mesh setup ---------- + // -------------------------------- + // vertices + bool force = meshState().bindMeshBufferInternal(mesh.vertexBufferObject) + || (mesh.vertices != nullptr); + meshState().bindPositionVertexPointer(force, mesh.vertices, mesh.stride); + + // indices + meshState().bindIndicesBufferInternal(mesh.indexBufferObject); + if (glop.mesh.vertexFlags & kTextureCoord_Attrib) { // TODO: support textures LOG_ALWAYS_FATAL("textures not yet supported"); @@ -232,41 +256,43 @@ void RenderState::render(const Glop& glop) { meshState().disableTexCoordsVertexArray(); } if (glop.mesh.vertexFlags & kColor_Attrib) { - LOG_ALWAYS_FATAL("color attribute not yet supported"); + LOG_ALWAYS_FATAL("color vertex attribute not yet supported"); // TODO: enable color, disable when done } + int alphaSlot = -1; if (glop.mesh.vertexFlags & kAlpha_Attrib) { - LOG_ALWAYS_FATAL("alpha attribute not yet supported"); - // TODO: enable alpha attribute, disable when done + const void* alphaCoords = ((const GLbyte*) glop.mesh.vertices) + kVertexAlphaOffset; + alphaSlot = shader.program->getAttrib("vtxAlpha"); + glEnableVertexAttribArray(alphaSlot); + glVertexAttribPointer(alphaSlot, 1, GL_FLOAT, GL_FALSE, kAlphaVertexStride, alphaCoords); } - /** - * Hard-coded vertex assumptions: - * - required - * - xy floats - * - 0 offset - * - in VBO - */ - bool force = meshState().bindMeshBuffer(mesh.vertexBufferObject); - meshState().bindPositionVertexPointer(force, nullptr, mesh.stride); - - /** - * Hard-coded index assumptions: - * - optional - * - 0 offset - * - in IBO - */ - meshState().bindIndicesBufferInternal(mesh.indexBufferObject); - + // ------------------------------------ // ---------- GL state setup ---------- + // ------------------------------------ blend().setFactors(glop.blend.src, glop.blend.dst); - if (mesh.indexBufferObject) { - glDrawElements(glop.mesh.primitiveMode, glop.mesh.vertexCount, GL_UNSIGNED_BYTE, nullptr); + // ------------------------------------ + // ---------- GL state setup ---------- + // ------------------------------------ + if (mesh.indexBufferObject || mesh.indices) { + glDrawElements(glop.mesh.primitiveMode, glop.mesh.vertexCount, + GL_UNSIGNED_SHORT, mesh.indices); } else { - glDrawArrays(GL_TRIANGLE_STRIP, 0, glop.mesh.vertexCount); + glDrawArrays(glop.mesh.primitiveMode, 0, glop.mesh.vertexCount); + } + + if (glop.mesh.vertexFlags & kAlpha_Attrib) { + glDisableVertexAttribArray(alphaSlot); } } +void RenderState::dump() { + blend().dump(); + meshState().dump(); + scissor().dump(); + stencil().dump(); +} + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h index 2e28ff6..4fd792c 100644 --- a/libs/hwui/renderstate/RenderState.h +++ b/libs/hwui/renderstate/RenderState.h @@ -91,6 +91,8 @@ public: MeshState& meshState() { return *mMeshState; } Scissor& scissor() { return *mScissor; } Stencil& stencil() { return *mStencil; } + + void dump(); private: friend class renderthread::RenderThread; friend class Caches; @@ -99,9 +101,6 @@ private: void resumeFromFunctorInvoke(); void assertOnGLThread(); - void setupVertexAttributes(const Glop& glop); - void tearDownVertexAttributes(const Glop& glop); - RenderState(renderthread::RenderThread& thread); ~RenderState(); diff --git a/libs/hwui/renderstate/Scissor.cpp b/libs/hwui/renderstate/Scissor.cpp index 66c31a2..95dcd18 100644 --- a/libs/hwui/renderstate/Scissor.cpp +++ b/libs/hwui/renderstate/Scissor.cpp @@ -15,6 +15,8 @@ */ #include "renderstate/Scissor.h" +#include <utils/Log.h> + namespace android { namespace uirenderer { @@ -79,6 +81,11 @@ void Scissor::invalidate() { reset(); } +void Scissor::dump() { + ALOGD("Scissor: enabled %d, %d %d %d %d", + mEnabled, mScissorX, mScissorY, mScissorWidth, mScissorHeight); +} + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderstate/Scissor.h b/libs/hwui/renderstate/Scissor.h index cc8b3dd..b37ec58 100644 --- a/libs/hwui/renderstate/Scissor.h +++ b/libs/hwui/renderstate/Scissor.h @@ -29,6 +29,7 @@ public: bool set(GLint x, GLint y, GLint width, GLint height); void reset(); bool isEnabled() { return mEnabled; } + void dump(); private: Scissor(); void invalidate(); diff --git a/libs/hwui/renderstate/Stencil.cpp b/libs/hwui/renderstate/Stencil.cpp index acbed14..cedb233 100644 --- a/libs/hwui/renderstate/Stencil.cpp +++ b/libs/hwui/renderstate/Stencil.cpp @@ -14,10 +14,12 @@ * limitations under the License. */ +#include "renderstate/Stencil.h" + +#include "Caches.h" #include "Debug.h" #include "Extensions.h" #include "Properties.h" -#include "renderstate/Stencil.h" #include <GLES2/gl2ext.h> @@ -42,7 +44,7 @@ uint8_t Stencil::getStencilSize() { GLenum Stencil::getSmallestStencilFormat() { #if !DEBUG_STENCIL - const Extensions& extensions = Extensions::getInstance(); + const Extensions& extensions = Caches::getInstance().extensions(); if (extensions.has1BitStencil()) { return GL_STENCIL_INDEX1_OES; } else if (extensions.has4BitStencil()) { @@ -123,5 +125,9 @@ void Stencil::disable() { } } +void Stencil::dump() { + ALOGD("Stencil: state %d", mState); +} + }; // namespace uirenderer }; // namespace android diff --git a/libs/hwui/renderstate/Stencil.h b/libs/hwui/renderstate/Stencil.h index 20bb955..a88beae 100644 --- a/libs/hwui/renderstate/Stencil.h +++ b/libs/hwui/renderstate/Stencil.h @@ -98,6 +98,8 @@ public: return mState == kTest; } + void dump(); + private: void enable(); diff --git a/libs/hwui/tests/main.cpp b/libs/hwui/tests/main.cpp index 1e7ba23..5f445f4 100644 --- a/libs/hwui/tests/main.cpp +++ b/libs/hwui/tests/main.cpp @@ -201,6 +201,45 @@ private: } }; +class OvalAnimation : public TreeContentAnimation { +public: + sp<RenderNode> card; + void createContent(int width, int height, DisplayListRenderer* renderer) override { + android::uirenderer::Rect DUMMY; + + renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); + renderer->insertReorderBarrier(true); + + card = createCard(40, 40, 200, 200); + renderer->drawRenderNode(card.get(), DUMMY, 0); + + renderer->insertReorderBarrier(false); + } + + void doFrame(int frameNr) override { + card->mutateStagingProperties().setTranslationX(frameNr); + card->mutateStagingProperties().setTranslationY(frameNr); + card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); + } +private: + sp<RenderNode> createCard(int x, int y, int width, int height) { + sp<RenderNode> node = new RenderNode(); + node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height); + node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); + + DisplayListRenderer* renderer = startRecording(node.get()); + renderer->drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode); + + SkPaint paint; + paint.setAntiAlias(true); + paint.setColor(0xFF00FFFF); + renderer->drawOval(0, 0, width, height, paint); + + endRecording(renderer, node.get()); + return node; + } +}; + struct cstr_cmp { bool operator()(const char *a, const char *b) const { return std::strcmp(a, b) < 0; @@ -212,6 +251,7 @@ typedef void (*testProc)(); std::map<const char*, testProc, cstr_cmp> gTestMap { {"shadowgrid", TreeContentAnimation::run<ShadowGridAnimation>}, {"rectgrid", TreeContentAnimation::run<RectGridAnimation> }, + {"oval", TreeContentAnimation::run<OvalAnimation> }, }; int main(int argc, char* argv[]) { diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h index 2091705..679e2bf 100644 --- a/libs/hwui/utils/PaintUtils.h +++ b/libs/hwui/utils/PaintUtils.h @@ -52,6 +52,13 @@ public: && getXfermode(paint.getXfermode()) == SkXfermode::kSrcOver_Mode; } + static bool isBlendedShader(const SkShader* shader) { + if (shader == nullptr) { + return false; + } + return !shader->isOpaque(); + } + static bool isBlendedColorFilter(const SkColorFilter* filter) { if (filter == nullptr) { return false; |