diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp index 757c12bdd9297..a0d5faed270d7 100644 --- a/libs/hwui/BakedOpRenderer.cpp +++ b/libs/hwui/BakedOpRenderer.cpp @@ -60,10 +60,18 @@ void BakedOpRenderer::startRepaintLayer(OffscreenBuffer* offscreenBuffer, const } void BakedOpRenderer::endLayer() { - mRenderTarget.offscreenBuffer->updateMeshFromRegion(); - mRenderTarget.offscreenBuffer = nullptr; + if (mRenderTarget.stencil) { + // if stencil was used for clipping, detach it and return it to pool + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0); + LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "glfbrb endlayer failed"); + mCaches.renderBufferCache.put(mRenderTarget.stencil); + mRenderTarget.stencil = nullptr; + } mRenderTarget.lastStencilClip = nullptr; + mRenderTarget.offscreenBuffer->updateMeshFromRegion(); + mRenderTarget.offscreenBuffer = nullptr; // It's in drawLayerOp's hands now. + // Detach the texture from the FBO glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "endLayer FAILED"); @@ -75,7 +83,6 @@ void BakedOpRenderer::startFrame(uint32_t width, uint32_t height, const Rect& re LOG_ALWAYS_FATAL_IF(mRenderTarget.frameBufferId != 0, "primary framebufferId must be 0"); mRenderState.bindFramebuffer(0); setViewport(width, height); - mCaches.clearGarbage(); if (!mOpaque) { clearColorBuffer(repaintRect); @@ -113,6 +120,7 @@ void BakedOpRenderer::endFrame(const Rect& repaintRect) { mRenderState.stencil().disable(); } + mCaches.clearGarbage(); mCaches.pathCache.trim(); mCaches.tessellationCache.trim(); @@ -232,28 +240,45 @@ void BakedOpRenderer::prepareRender(const Rect* dirtyBounds, const ClipBase* cli mRenderState.scissor().setEnabled(clip != nullptr); if (clip) { mRenderState.scissor().set(mRenderTarget.viewportHeight, clip->rect); - if (CC_LIKELY(!Properties::debugOverdraw)) { - // only modify stencil mode and content when it's not used for overdraw visualization - if (CC_UNLIKELY(clip->mode != ClipMode::Rectangle)) { - // NOTE: this pointer check is only safe for non-rect clips, - // since rect clips may be created on the stack - if (mRenderTarget.lastStencilClip != clip) { - // Stencil needed, but current stencil isn't up to date - mRenderTarget.lastStencilClip = clip; + } - if (mRenderTarget.offscreenBuffer) { - LOG_ALWAYS_FATAL("prepare layer stencil"); - } + if (CC_LIKELY(!Properties::debugOverdraw)) { + // only modify stencil mode and content when it's not used for overdraw visualization + if (CC_UNLIKELY(clip && clip->mode != ClipMode::Rectangle)) { + // NOTE: this pointer check is only safe for non-rect clips, + // since rect clips may be created on the stack + if (mRenderTarget.lastStencilClip != clip) { + // Stencil needed, but current stencil isn't up to date + mRenderTarget.lastStencilClip = clip; - if (clip->mode == ClipMode::RectangleList) { - setupStencilRectList(clip); - } else { - setupStencilRegion(clip); - } + if (mRenderTarget.frameBufferId != 0 && !mRenderTarget.stencil) { + OffscreenBuffer* layer = mRenderTarget.offscreenBuffer; + mRenderTarget.stencil = mCaches.renderBufferCache.get( + Stencil::getLayerStencilFormat(), + layer->texture.width, layer->texture.height); + // stencil is bound + allocated - associate it with current FBO + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, mRenderTarget.stencil->getName()); + } + + if (clip->mode == ClipMode::RectangleList) { + setupStencilRectList(clip); + } else { + setupStencilRegion(clip); } } else { - mRenderState.stencil().disable(); + // stencil is up to date - just need to ensure it's enabled (since an unclipped + // or scissor-only clipped op may have been drawn, disabling the stencil) + int incrementThreshold = 0; + if (CC_LIKELY(clip->mode == ClipMode::RectangleList)) { + auto&& rectList = reinterpret_cast(clip)->rectList; + incrementThreshold = rectList.getTransformedRectanglesCount(); + } + mRenderState.stencil().enableTest(incrementThreshold); } + } else { + // either scissor or no clip, so disable stencil test + mRenderState.stencil().disable(); } } diff --git a/libs/hwui/BakedOpRenderer.h b/libs/hwui/BakedOpRenderer.h index 5bd2c0a1fd5fc..65e8b29a9ed27 100644 --- a/libs/hwui/BakedOpRenderer.h +++ b/libs/hwui/BakedOpRenderer.h @@ -95,12 +95,24 @@ private: // render target state - setup by start/end layer/frame // only valid to use in between start/end pairs. struct { + // If not drawing to a layer: fbo = 0, offscreenBuffer = null, + // Otherwise these refer to currently painting layer's state GLuint frameBufferId = 0; OffscreenBuffer* offscreenBuffer = nullptr; + + // Used when drawing to a layer and using stencil clipping. otherwise null. + RenderBuffer* stencil = nullptr; + + // value representing the ClipRectList* or ClipRegion* currently stored in + // the stencil of the current render target + const ClipBase* lastStencilClip = nullptr; + + // Size of renderable region in current render target - for layers, may not match actual + // bounds of FBO texture. offscreenBuffer->texture has this information. uint32_t viewportWidth = 0; uint32_t viewportHeight = 0; + Matrix4 orthoMatrix; - const ClipBase* lastStencilClip = nullptr; } mRenderTarget; const LightInfo mLightInfo; diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp index 2507ff3771338..45fc16cc31369 100644 --- a/libs/hwui/GlopBuilder.cpp +++ b/libs/hwui/GlopBuilder.cpp @@ -78,7 +78,7 @@ GlopBuilder& GlopBuilder::setMeshTexturedIndexedVbo(GLuint vbo, GLsizei elementC mOutGlop->mesh.vertices = { vbo, VertexAttribFlags::TextureCoord, - nullptr, nullptr, nullptr, + nullptr, (const void*) kMeshTextureOffset, nullptr, kTextureVertexStride }; mOutGlop->mesh.elementCount = elementCount; return *this; diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h index 3bfe10d177385..1971530d2caac 100644 --- a/libs/hwui/RecordedOp.h +++ b/libs/hwui/RecordedOp.h @@ -391,9 +391,9 @@ struct LayerOp : RecordedOp { LayerOp(BASE_PARAMS, OffscreenBuffer** layerHandle) : SUPER_PAINTLESS(LayerOp) , layerHandle(layerHandle) - , alpha(paint->getAlpha() / 255.0f) + , alpha(paint ? paint->getAlpha() / 255.0f : 1.0f) , mode(PaintUtils::getXfermodeDirect(paint)) - , colorFilter(paint->getColorFilter()) + , colorFilter(paint ? paint->getColorFilter() : nullptr) , destroy(true) {} LayerOp(RenderNode& node) diff --git a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp index 38788f09852eb..db6402ce136fe 100644 --- a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp @@ -30,8 +30,6 @@ public: sp card; void createContent(int width, int height, TestCanvas& canvas) override { canvas.drawColor(Color::White, SkXfermode::kSrcOver_Mode); - canvas.insertReorderBarrier(true); - card = TestUtils::createNode(0, 0, 200, 400, [](RenderProperties& props, TestCanvas& canvas) { canvas.save(SkCanvas::kMatrixClip_SaveFlag); @@ -53,10 +51,12 @@ public: canvas.drawColor(Color::Red_500, SkXfermode::kSrcOver_Mode); } canvas.restore(); + + // put on a layer, to test stencil attachment + props.mutateLayerProperties().setType(LayerType::RenderLayer); + props.setAlpha(0.9f); }); canvas.drawRenderNode(card.get()); - - canvas.insertReorderBarrier(false); } void doFrame(int frameNr) override { int curFrame = frameNr % 150;