Loading libs/hwui/Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -108,6 +108,7 @@ ifeq (true, $(HWUI_NEW_OPS)) hwui_src_files += \ BakedOpDispatcher.cpp \ BakedOpRenderer.cpp \ BakedOpState.cpp \ OpReorderer.cpp \ RecordingCanvas.cpp Loading libs/hwui/BakedOpDispatcher.cpp +12 −8 Original line number Diff line number Diff line Loading @@ -79,7 +79,9 @@ void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer, .setTransform(Matrix4::identity(), TransformFlags::None) .setModelViewIdentityEmptyBounds() .build(); renderer.renderGlop(nullptr, opList.clipSideFlags ? &opList.clip : nullptr, glop); ClipRect renderTargetClip(opList.clip); const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr; renderer.renderGlop(nullptr, clip, glop); } void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer, Loading Loading @@ -183,7 +185,9 @@ void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer, .setTransform(Matrix4::identity(), TransformFlags::None) .setModelViewIdentityEmptyBounds() .build(); renderer.renderGlop(nullptr, opList.clipSideFlags ? &opList.clip : nullptr, glop); ClipRect renderTargetClip(opList.clip); const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr; renderer.renderGlop(nullptr, clip, glop); } static void renderTextShadow(BakedOpRenderer& renderer, FontRenderer& fontRenderer, Loading Loading @@ -224,7 +228,7 @@ enum class TextRenderType { }; static void renderTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state, const Rect* renderClip, TextRenderType renderType) { const ClipBase* renderClip, TextRenderType renderType) { FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer(); if (CC_UNLIKELY(PaintUtils::hasTextShadow(op.paint))) { Loading Loading @@ -272,7 +276,7 @@ static void renderTextOp(BakedOpRenderer& renderer, const TextOp& op, const Bake bool forceFinish = (renderType == TextRenderType::Flush); bool mustDirtyRenderTarget = renderer.offscreenRenderTarget(); const Rect* localOpClip = pureTranslate ? &state.computedState.clipRect : nullptr; const Rect* localOpClip = pureTranslate ? &state.computedState.clipRect() : nullptr; fontRenderer.renderPosText(op.paint, localOpClip, (const char*) op.glyphs, op.glyphCount, x, y, op.positions, mustDirtyRenderTarget ? &layerBounds : nullptr, &functor, forceFinish); Loading @@ -287,7 +291,8 @@ static void renderTextOp(BakedOpRenderer& renderer, const TextOp& op, const Bake void BakedOpDispatcher::onMergedTextOps(BakedOpRenderer& renderer, const MergedBakedOpList& opList) { const Rect* clip = opList.clipSideFlags ? &opList.clip : nullptr; ClipRect renderTargetClip(opList.clip); const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr; for (size_t i = 0; i < opList.count; i++) { const BakedOpState& state = *(opList.states[i]); const TextOp& op = *(static_cast<const TextOp*>(state.op)); Loading Loading @@ -701,14 +706,13 @@ void BakedOpDispatcher::onSimpleRectsOp(BakedOpRenderer& renderer, const SimpleR } void BakedOpDispatcher::onTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state) { const Rect* clip = state.computedState.clipSideFlags ? &state.computedState.clipRect : nullptr; renderTextOp(renderer, op, state, clip, TextRenderType::Flush); renderTextOp(renderer, op, state, state.computedState.getClipIfNeeded(), TextRenderType::Flush); } void BakedOpDispatcher::onTextOnPathOp(BakedOpRenderer& renderer, const TextOnPathOp& op, const BakedOpState& state) { // Note: can't trust clipSideFlags since we record with unmappedBounds == clip. // TODO: respect clipSideFlags, once we record with bounds const Rect* renderTargetClip = &state.computedState.clipRect; auto renderTargetClip = state.computedState.clipState; FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer(); fontRenderer.setFont(op.paint, SkMatrix::I()); Loading libs/hwui/BakedOpRenderer.cpp +133 −8 Original line number Diff line number Diff line Loading @@ -62,15 +62,17 @@ void BakedOpRenderer::startRepaintLayer(OffscreenBuffer* offscreenBuffer, const void BakedOpRenderer::endLayer() { mRenderTarget.offscreenBuffer->updateMeshFromRegion(); mRenderTarget.offscreenBuffer = nullptr; mRenderTarget.lastStencilClip = nullptr; // 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"); mRenderState.deleteFramebuffer(mRenderTarget.frameBufferId); mRenderTarget.frameBufferId = -1; mRenderTarget.frameBufferId = 0; } void BakedOpRenderer::startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) { LOG_ALWAYS_FATAL_IF(mRenderTarget.frameBufferId != 0, "primary framebufferId must be 0"); mRenderState.bindFramebuffer(0); setViewport(width, height); mCaches.clearGarbage(); Loading @@ -78,9 +80,39 @@ void BakedOpRenderer::startFrame(uint32_t width, uint32_t height, const Rect& re if (!mOpaque) { clearColorBuffer(repaintRect); } mRenderState.debugOverdraw(true, true); } void BakedOpRenderer::endFrame(const Rect& repaintRect) { if (CC_UNLIKELY(Properties::debugOverdraw)) { ClipRect overdrawClip(repaintRect); Rect viewportRect(mRenderTarget.viewportWidth, mRenderTarget.viewportHeight); // overdraw visualization for (int i = 1; i <= 4; i++) { if (i < 4) { // nth level of overdraw tests for n+1 draws per pixel mRenderState.stencil().enableDebugTest(i + 1, false); } else { // 4th level tests for 4 or higher draws per pixel mRenderState.stencil().enableDebugTest(4, true); } SkPaint paint; paint.setColor(mCaches.getOverdrawColor(i)); Glop glop; GlopBuilder(mRenderState, mCaches, &glop) .setRoundRectClipState(nullptr) .setMeshUnitQuad() .setFillPaint(paint, 1.0f) .setTransform(Matrix4::identity(), TransformFlags::None) .setModelViewMapUnitToRect(viewportRect) .build(); renderGlop(nullptr, &overdrawClip, glop); } mRenderState.stencil().disable(); } void BakedOpRenderer::endFrame() { mCaches.pathCache.trim(); mCaches.tessellationCache.trim(); Loading Loading @@ -128,12 +160,104 @@ Texture* BakedOpRenderer::getTexture(const SkBitmap* bitmap) { return texture; } void BakedOpRenderer::prepareRender(const Rect* dirtyBounds, const Rect* clip) { // clears and re-fills stencil with provided rendertarget space quads, // and then put stencil into test mode void BakedOpRenderer::setupStencilQuads(std::vector<Vertex>& quadVertices, int incrementThreshold) { mRenderState.stencil().enableWrite(incrementThreshold); mRenderState.stencil().clear(); Glop glop; GlopBuilder(mRenderState, mCaches, &glop) .setRoundRectClipState(nullptr) .setMeshIndexedQuads(quadVertices.data(), quadVertices.size() / 4) .setFillBlack() .setTransform(Matrix4::identity(), TransformFlags::None) .setModelViewIdentityEmptyBounds() .build(); mRenderState.render(glop, mRenderTarget.orthoMatrix); mRenderState.stencil().enableTest(incrementThreshold); } void BakedOpRenderer::setupStencilRectList(const ClipBase* clip) { auto&& rectList = reinterpret_cast<const ClipRectList*>(clip)->rectList; int quadCount = rectList.getTransformedRectanglesCount(); std::vector<Vertex> rectangleVertices; rectangleVertices.reserve(quadCount * 4); for (int i = 0; i < quadCount; i++) { const TransformedRectangle& tr(rectList.getTransformedRectangle(i)); const Matrix4& transform = tr.getTransform(); Rect bounds = tr.getBounds(); if (transform.rectToRect()) { // If rectToRect, can simply map bounds before storing verts transform.mapRect(bounds); bounds.doIntersect(clip->rect); if (bounds.isEmpty()) { continue; // will be outside of scissor, skip } } rectangleVertices.push_back(Vertex{bounds.left, bounds.top}); rectangleVertices.push_back(Vertex{bounds.right, bounds.top}); rectangleVertices.push_back(Vertex{bounds.left, bounds.bottom}); rectangleVertices.push_back(Vertex{bounds.right, bounds.bottom}); if (!transform.rectToRect()) { // If not rectToRect, must map each point individually for (auto cur = rectangleVertices.end() - 4; cur < rectangleVertices.end(); cur++) { transform.mapPoint(cur->x, cur->y); } } } setupStencilQuads(rectangleVertices, rectList.getTransformedRectanglesCount()); } void BakedOpRenderer::setupStencilRegion(const ClipBase* clip) { auto&& region = reinterpret_cast<const ClipRegion*>(clip)->region; std::vector<Vertex> regionVertices; SkRegion::Cliperator it(region, clip->rect.toSkIRect()); while (!it.done()) { const SkIRect& r = it.rect(); regionVertices.push_back(Vertex{(float)r.fLeft, (float)r.fTop}); regionVertices.push_back(Vertex{(float)r.fRight, (float)r.fTop}); regionVertices.push_back(Vertex{(float)r.fLeft, (float)r.fBottom}); regionVertices.push_back(Vertex{(float)r.fRight, (float)r.fBottom}); it.next(); } setupStencilQuads(regionVertices, 0); } void BakedOpRenderer::prepareRender(const Rect* dirtyBounds, const ClipBase* clip) { // prepare scissor / stencil mRenderState.scissor().setEnabled(clip != nullptr); if (clip) { mRenderState.scissor().set(clip->left, mRenderTarget.viewportHeight - clip->bottom, clip->getWidth(), clip->getHeight()); 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 (clip->mode == ClipMode::RectangleList) { setupStencilRectList(clip); } else { setupStencilRegion(clip); } } } else { mRenderState.stencil().disable(); } } } // dirty offscreenbuffer if (dirtyBounds && mRenderTarget.offscreenBuffer) { // register layer damage to draw-back region android::Rect dirty(dirtyBounds->left, dirtyBounds->top, Loading @@ -142,17 +266,18 @@ void BakedOpRenderer::prepareRender(const Rect* dirtyBounds, const Rect* clip) { } } void BakedOpRenderer::renderGlop(const Rect* dirtyBounds, const Rect* clip, const Glop& glop) { void BakedOpRenderer::renderGlop(const Rect* dirtyBounds, const ClipBase* clip, const Glop& glop) { prepareRender(dirtyBounds, clip); mRenderState.render(glop, mRenderTarget.orthoMatrix); if (!mRenderTarget.frameBufferId) mHasDrawn = true; } void BakedOpRenderer::renderFunctor(const FunctorOp& op, const BakedOpState& state) { prepareRender(&state.computedState.clippedBounds, &state.computedState.clipRect); prepareRender(&state.computedState.clippedBounds, state.computedState.getClipIfNeeded()); DrawGlInfo info; auto&& clip = state.computedState.clipRect; auto&& clip = state.computedState.clipRect(); info.clipLeft = clip.left; info.clipTop = clip.top; info.clipRight = clip.right; Loading libs/hwui/BakedOpRenderer.h +9 −5 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ class Caches; struct Glop; class Layer; class RenderState; struct ClipBase; /** * Main rendering manager for a collection of work - one frame + any contained FBOs. Loading Loading @@ -59,7 +60,7 @@ public: Caches& caches() { return mCaches; } void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect); void endFrame(); void endFrame(const Rect& repaintRect); OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height); void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect); void endLayer(); Loading @@ -68,21 +69,23 @@ public: const LightInfo& getLightInfo() const { return mLightInfo; } void renderGlop(const BakedOpState& state, const Glop& glop) { bool useScissor = state.computedState.clipSideFlags != OpClipSideFlags::None; renderGlop(&state.computedState.clippedBounds, useScissor ? &state.computedState.clipRect : nullptr, state.computedState.getClipIfNeeded(), glop); } void renderFunctor(const FunctorOp& op, const BakedOpState& state); void renderGlop(const Rect* dirtyBounds, const Rect* clip, const Glop& glop); void renderGlop(const Rect* dirtyBounds, const ClipBase* clip, const Glop& glop); bool offscreenRenderTarget() { return mRenderTarget.offscreenBuffer != nullptr; } void dirtyRenderTarget(const Rect& dirtyRect); bool didDraw() const { return mHasDrawn; } private: void setViewport(uint32_t width, uint32_t height); void clearColorBuffer(const Rect& clearRect); void prepareRender(const Rect* dirtyBounds, const Rect* clip); void prepareRender(const Rect* dirtyBounds, const ClipBase* clip); void setupStencilRectList(const ClipBase* clip); void setupStencilRegion(const ClipBase* clip); void setupStencilQuads(std::vector<Vertex>& quadVertices, int incrementThreshold); RenderState& mRenderState; Caches& mCaches; Loading @@ -97,6 +100,7 @@ private: uint32_t viewportWidth = 0; uint32_t viewportHeight = 0; Matrix4 orthoMatrix; const ClipBase* lastStencilClip = nullptr; } mRenderTarget; const LightInfo mLightInfo; Loading libs/hwui/BakedOpState.cpp 0 → 100644 +79 −0 Original line number Diff line number Diff line /* * 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 "BakedOpState.h" #include "ClipArea.h" namespace android { namespace uirenderer { ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot, const RecordedOp& recordedOp, bool expandForStroke) { // resolvedMatrix = parentMatrix * localMatrix transform.loadMultiply(*snapshot.transform, recordedOp.localMatrix); // resolvedClippedBounds = intersect(resolvedMatrix * opBounds, resolvedClipRect) clippedBounds = recordedOp.unmappedBounds; if (CC_UNLIKELY(expandForStroke)) { // account for non-hairline stroke clippedBounds.outset(recordedOp.paint->getStrokeWidth() * 0.5f); } transform.mapRect(clippedBounds); if (CC_UNLIKELY(expandForStroke && (!transform.isPureTranslate() || recordedOp.paint->getStrokeWidth() < 1.0f))) { // account for hairline stroke when stroke may be < 1 scaled pixel // Non translate || strokeWidth < 1 is conservative, but will cover all cases clippedBounds.outset(0.5f); } // resolvedClipRect = intersect(parentMatrix * localClip, parentClip) clipState = snapshot.mutateClipArea().serializeIntersectedClip(allocator, recordedOp.localClip, *(snapshot.transform)); LOG_ALWAYS_FATAL_IF(!clipState, "must clip!"); const Rect& clipRect = clipState->rect; if (CC_UNLIKELY(clipRect.isEmpty() || !clippedBounds.intersects(clipRect))) { // Rejected based on either empty clip, or bounds not intersecting with clip if (clipState) { allocator.rewindIfLastAlloc(clipState); clipState = nullptr; } clippedBounds.setEmpty(); } else { // Not rejected! compute true clippedBounds and clipSideFlags if (clipRect.left > clippedBounds.left) clipSideFlags |= OpClipSideFlags::Left; if (clipRect.top > clippedBounds.top) clipSideFlags |= OpClipSideFlags::Top; if (clipRect.right < clippedBounds.right) clipSideFlags |= OpClipSideFlags::Right; if (clipRect.bottom < clippedBounds.bottom) clipSideFlags |= OpClipSideFlags::Bottom; clippedBounds.doIntersect(clipRect); } } ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot) { transform = *snapshot.transform; // Since the op doesn't have known bounds, we conservatively set the mapped bounds // to the current clipRect, and clipSideFlags to Full. clipState = snapshot.mutateClipArea().serializeClip(allocator); LOG_ALWAYS_FATAL_IF(!clipState, "clipState required"); clippedBounds = clipState->rect; transform.mapRect(clippedBounds); clipSideFlags = OpClipSideFlags::Full; } } // namespace uirenderer } // namespace android Loading
libs/hwui/Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -108,6 +108,7 @@ ifeq (true, $(HWUI_NEW_OPS)) hwui_src_files += \ BakedOpDispatcher.cpp \ BakedOpRenderer.cpp \ BakedOpState.cpp \ OpReorderer.cpp \ RecordingCanvas.cpp Loading
libs/hwui/BakedOpDispatcher.cpp +12 −8 Original line number Diff line number Diff line Loading @@ -79,7 +79,9 @@ void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer, .setTransform(Matrix4::identity(), TransformFlags::None) .setModelViewIdentityEmptyBounds() .build(); renderer.renderGlop(nullptr, opList.clipSideFlags ? &opList.clip : nullptr, glop); ClipRect renderTargetClip(opList.clip); const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr; renderer.renderGlop(nullptr, clip, glop); } void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer, Loading Loading @@ -183,7 +185,9 @@ void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer, .setTransform(Matrix4::identity(), TransformFlags::None) .setModelViewIdentityEmptyBounds() .build(); renderer.renderGlop(nullptr, opList.clipSideFlags ? &opList.clip : nullptr, glop); ClipRect renderTargetClip(opList.clip); const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr; renderer.renderGlop(nullptr, clip, glop); } static void renderTextShadow(BakedOpRenderer& renderer, FontRenderer& fontRenderer, Loading Loading @@ -224,7 +228,7 @@ enum class TextRenderType { }; static void renderTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state, const Rect* renderClip, TextRenderType renderType) { const ClipBase* renderClip, TextRenderType renderType) { FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer(); if (CC_UNLIKELY(PaintUtils::hasTextShadow(op.paint))) { Loading Loading @@ -272,7 +276,7 @@ static void renderTextOp(BakedOpRenderer& renderer, const TextOp& op, const Bake bool forceFinish = (renderType == TextRenderType::Flush); bool mustDirtyRenderTarget = renderer.offscreenRenderTarget(); const Rect* localOpClip = pureTranslate ? &state.computedState.clipRect : nullptr; const Rect* localOpClip = pureTranslate ? &state.computedState.clipRect() : nullptr; fontRenderer.renderPosText(op.paint, localOpClip, (const char*) op.glyphs, op.glyphCount, x, y, op.positions, mustDirtyRenderTarget ? &layerBounds : nullptr, &functor, forceFinish); Loading @@ -287,7 +291,8 @@ static void renderTextOp(BakedOpRenderer& renderer, const TextOp& op, const Bake void BakedOpDispatcher::onMergedTextOps(BakedOpRenderer& renderer, const MergedBakedOpList& opList) { const Rect* clip = opList.clipSideFlags ? &opList.clip : nullptr; ClipRect renderTargetClip(opList.clip); const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr; for (size_t i = 0; i < opList.count; i++) { const BakedOpState& state = *(opList.states[i]); const TextOp& op = *(static_cast<const TextOp*>(state.op)); Loading Loading @@ -701,14 +706,13 @@ void BakedOpDispatcher::onSimpleRectsOp(BakedOpRenderer& renderer, const SimpleR } void BakedOpDispatcher::onTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state) { const Rect* clip = state.computedState.clipSideFlags ? &state.computedState.clipRect : nullptr; renderTextOp(renderer, op, state, clip, TextRenderType::Flush); renderTextOp(renderer, op, state, state.computedState.getClipIfNeeded(), TextRenderType::Flush); } void BakedOpDispatcher::onTextOnPathOp(BakedOpRenderer& renderer, const TextOnPathOp& op, const BakedOpState& state) { // Note: can't trust clipSideFlags since we record with unmappedBounds == clip. // TODO: respect clipSideFlags, once we record with bounds const Rect* renderTargetClip = &state.computedState.clipRect; auto renderTargetClip = state.computedState.clipState; FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer(); fontRenderer.setFont(op.paint, SkMatrix::I()); Loading
libs/hwui/BakedOpRenderer.cpp +133 −8 Original line number Diff line number Diff line Loading @@ -62,15 +62,17 @@ void BakedOpRenderer::startRepaintLayer(OffscreenBuffer* offscreenBuffer, const void BakedOpRenderer::endLayer() { mRenderTarget.offscreenBuffer->updateMeshFromRegion(); mRenderTarget.offscreenBuffer = nullptr; mRenderTarget.lastStencilClip = nullptr; // 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"); mRenderState.deleteFramebuffer(mRenderTarget.frameBufferId); mRenderTarget.frameBufferId = -1; mRenderTarget.frameBufferId = 0; } void BakedOpRenderer::startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) { LOG_ALWAYS_FATAL_IF(mRenderTarget.frameBufferId != 0, "primary framebufferId must be 0"); mRenderState.bindFramebuffer(0); setViewport(width, height); mCaches.clearGarbage(); Loading @@ -78,9 +80,39 @@ void BakedOpRenderer::startFrame(uint32_t width, uint32_t height, const Rect& re if (!mOpaque) { clearColorBuffer(repaintRect); } mRenderState.debugOverdraw(true, true); } void BakedOpRenderer::endFrame(const Rect& repaintRect) { if (CC_UNLIKELY(Properties::debugOverdraw)) { ClipRect overdrawClip(repaintRect); Rect viewportRect(mRenderTarget.viewportWidth, mRenderTarget.viewportHeight); // overdraw visualization for (int i = 1; i <= 4; i++) { if (i < 4) { // nth level of overdraw tests for n+1 draws per pixel mRenderState.stencil().enableDebugTest(i + 1, false); } else { // 4th level tests for 4 or higher draws per pixel mRenderState.stencil().enableDebugTest(4, true); } SkPaint paint; paint.setColor(mCaches.getOverdrawColor(i)); Glop glop; GlopBuilder(mRenderState, mCaches, &glop) .setRoundRectClipState(nullptr) .setMeshUnitQuad() .setFillPaint(paint, 1.0f) .setTransform(Matrix4::identity(), TransformFlags::None) .setModelViewMapUnitToRect(viewportRect) .build(); renderGlop(nullptr, &overdrawClip, glop); } mRenderState.stencil().disable(); } void BakedOpRenderer::endFrame() { mCaches.pathCache.trim(); mCaches.tessellationCache.trim(); Loading Loading @@ -128,12 +160,104 @@ Texture* BakedOpRenderer::getTexture(const SkBitmap* bitmap) { return texture; } void BakedOpRenderer::prepareRender(const Rect* dirtyBounds, const Rect* clip) { // clears and re-fills stencil with provided rendertarget space quads, // and then put stencil into test mode void BakedOpRenderer::setupStencilQuads(std::vector<Vertex>& quadVertices, int incrementThreshold) { mRenderState.stencil().enableWrite(incrementThreshold); mRenderState.stencil().clear(); Glop glop; GlopBuilder(mRenderState, mCaches, &glop) .setRoundRectClipState(nullptr) .setMeshIndexedQuads(quadVertices.data(), quadVertices.size() / 4) .setFillBlack() .setTransform(Matrix4::identity(), TransformFlags::None) .setModelViewIdentityEmptyBounds() .build(); mRenderState.render(glop, mRenderTarget.orthoMatrix); mRenderState.stencil().enableTest(incrementThreshold); } void BakedOpRenderer::setupStencilRectList(const ClipBase* clip) { auto&& rectList = reinterpret_cast<const ClipRectList*>(clip)->rectList; int quadCount = rectList.getTransformedRectanglesCount(); std::vector<Vertex> rectangleVertices; rectangleVertices.reserve(quadCount * 4); for (int i = 0; i < quadCount; i++) { const TransformedRectangle& tr(rectList.getTransformedRectangle(i)); const Matrix4& transform = tr.getTransform(); Rect bounds = tr.getBounds(); if (transform.rectToRect()) { // If rectToRect, can simply map bounds before storing verts transform.mapRect(bounds); bounds.doIntersect(clip->rect); if (bounds.isEmpty()) { continue; // will be outside of scissor, skip } } rectangleVertices.push_back(Vertex{bounds.left, bounds.top}); rectangleVertices.push_back(Vertex{bounds.right, bounds.top}); rectangleVertices.push_back(Vertex{bounds.left, bounds.bottom}); rectangleVertices.push_back(Vertex{bounds.right, bounds.bottom}); if (!transform.rectToRect()) { // If not rectToRect, must map each point individually for (auto cur = rectangleVertices.end() - 4; cur < rectangleVertices.end(); cur++) { transform.mapPoint(cur->x, cur->y); } } } setupStencilQuads(rectangleVertices, rectList.getTransformedRectanglesCount()); } void BakedOpRenderer::setupStencilRegion(const ClipBase* clip) { auto&& region = reinterpret_cast<const ClipRegion*>(clip)->region; std::vector<Vertex> regionVertices; SkRegion::Cliperator it(region, clip->rect.toSkIRect()); while (!it.done()) { const SkIRect& r = it.rect(); regionVertices.push_back(Vertex{(float)r.fLeft, (float)r.fTop}); regionVertices.push_back(Vertex{(float)r.fRight, (float)r.fTop}); regionVertices.push_back(Vertex{(float)r.fLeft, (float)r.fBottom}); regionVertices.push_back(Vertex{(float)r.fRight, (float)r.fBottom}); it.next(); } setupStencilQuads(regionVertices, 0); } void BakedOpRenderer::prepareRender(const Rect* dirtyBounds, const ClipBase* clip) { // prepare scissor / stencil mRenderState.scissor().setEnabled(clip != nullptr); if (clip) { mRenderState.scissor().set(clip->left, mRenderTarget.viewportHeight - clip->bottom, clip->getWidth(), clip->getHeight()); 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 (clip->mode == ClipMode::RectangleList) { setupStencilRectList(clip); } else { setupStencilRegion(clip); } } } else { mRenderState.stencil().disable(); } } } // dirty offscreenbuffer if (dirtyBounds && mRenderTarget.offscreenBuffer) { // register layer damage to draw-back region android::Rect dirty(dirtyBounds->left, dirtyBounds->top, Loading @@ -142,17 +266,18 @@ void BakedOpRenderer::prepareRender(const Rect* dirtyBounds, const Rect* clip) { } } void BakedOpRenderer::renderGlop(const Rect* dirtyBounds, const Rect* clip, const Glop& glop) { void BakedOpRenderer::renderGlop(const Rect* dirtyBounds, const ClipBase* clip, const Glop& glop) { prepareRender(dirtyBounds, clip); mRenderState.render(glop, mRenderTarget.orthoMatrix); if (!mRenderTarget.frameBufferId) mHasDrawn = true; } void BakedOpRenderer::renderFunctor(const FunctorOp& op, const BakedOpState& state) { prepareRender(&state.computedState.clippedBounds, &state.computedState.clipRect); prepareRender(&state.computedState.clippedBounds, state.computedState.getClipIfNeeded()); DrawGlInfo info; auto&& clip = state.computedState.clipRect; auto&& clip = state.computedState.clipRect(); info.clipLeft = clip.left; info.clipTop = clip.top; info.clipRight = clip.right; Loading
libs/hwui/BakedOpRenderer.h +9 −5 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ class Caches; struct Glop; class Layer; class RenderState; struct ClipBase; /** * Main rendering manager for a collection of work - one frame + any contained FBOs. Loading Loading @@ -59,7 +60,7 @@ public: Caches& caches() { return mCaches; } void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect); void endFrame(); void endFrame(const Rect& repaintRect); OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height); void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect); void endLayer(); Loading @@ -68,21 +69,23 @@ public: const LightInfo& getLightInfo() const { return mLightInfo; } void renderGlop(const BakedOpState& state, const Glop& glop) { bool useScissor = state.computedState.clipSideFlags != OpClipSideFlags::None; renderGlop(&state.computedState.clippedBounds, useScissor ? &state.computedState.clipRect : nullptr, state.computedState.getClipIfNeeded(), glop); } void renderFunctor(const FunctorOp& op, const BakedOpState& state); void renderGlop(const Rect* dirtyBounds, const Rect* clip, const Glop& glop); void renderGlop(const Rect* dirtyBounds, const ClipBase* clip, const Glop& glop); bool offscreenRenderTarget() { return mRenderTarget.offscreenBuffer != nullptr; } void dirtyRenderTarget(const Rect& dirtyRect); bool didDraw() const { return mHasDrawn; } private: void setViewport(uint32_t width, uint32_t height); void clearColorBuffer(const Rect& clearRect); void prepareRender(const Rect* dirtyBounds, const Rect* clip); void prepareRender(const Rect* dirtyBounds, const ClipBase* clip); void setupStencilRectList(const ClipBase* clip); void setupStencilRegion(const ClipBase* clip); void setupStencilQuads(std::vector<Vertex>& quadVertices, int incrementThreshold); RenderState& mRenderState; Caches& mCaches; Loading @@ -97,6 +100,7 @@ private: uint32_t viewportWidth = 0; uint32_t viewportHeight = 0; Matrix4 orthoMatrix; const ClipBase* lastStencilClip = nullptr; } mRenderTarget; const LightInfo mLightInfo; Loading
libs/hwui/BakedOpState.cpp 0 → 100644 +79 −0 Original line number Diff line number Diff line /* * 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 "BakedOpState.h" #include "ClipArea.h" namespace android { namespace uirenderer { ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot, const RecordedOp& recordedOp, bool expandForStroke) { // resolvedMatrix = parentMatrix * localMatrix transform.loadMultiply(*snapshot.transform, recordedOp.localMatrix); // resolvedClippedBounds = intersect(resolvedMatrix * opBounds, resolvedClipRect) clippedBounds = recordedOp.unmappedBounds; if (CC_UNLIKELY(expandForStroke)) { // account for non-hairline stroke clippedBounds.outset(recordedOp.paint->getStrokeWidth() * 0.5f); } transform.mapRect(clippedBounds); if (CC_UNLIKELY(expandForStroke && (!transform.isPureTranslate() || recordedOp.paint->getStrokeWidth() < 1.0f))) { // account for hairline stroke when stroke may be < 1 scaled pixel // Non translate || strokeWidth < 1 is conservative, but will cover all cases clippedBounds.outset(0.5f); } // resolvedClipRect = intersect(parentMatrix * localClip, parentClip) clipState = snapshot.mutateClipArea().serializeIntersectedClip(allocator, recordedOp.localClip, *(snapshot.transform)); LOG_ALWAYS_FATAL_IF(!clipState, "must clip!"); const Rect& clipRect = clipState->rect; if (CC_UNLIKELY(clipRect.isEmpty() || !clippedBounds.intersects(clipRect))) { // Rejected based on either empty clip, or bounds not intersecting with clip if (clipState) { allocator.rewindIfLastAlloc(clipState); clipState = nullptr; } clippedBounds.setEmpty(); } else { // Not rejected! compute true clippedBounds and clipSideFlags if (clipRect.left > clippedBounds.left) clipSideFlags |= OpClipSideFlags::Left; if (clipRect.top > clippedBounds.top) clipSideFlags |= OpClipSideFlags::Top; if (clipRect.right < clippedBounds.right) clipSideFlags |= OpClipSideFlags::Right; if (clipRect.bottom < clippedBounds.bottom) clipSideFlags |= OpClipSideFlags::Bottom; clippedBounds.doIntersect(clipRect); } } ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot) { transform = *snapshot.transform; // Since the op doesn't have known bounds, we conservatively set the mapped bounds // to the current clipRect, and clipSideFlags to Full. clipState = snapshot.mutateClipArea().serializeClip(allocator); LOG_ALWAYS_FATAL_IF(!clipState, "clipState required"); clippedBounds = clipState->rect; transform.mapRect(clippedBounds); clipSideFlags = OpClipSideFlags::Full; } } // namespace uirenderer } // namespace android