diff options
author | 2016-05-12 17:48:51 -0700 | |
---|---|---|
committer | 2016-05-16 13:54:02 -0700 | |
commit | 37413289478a965336239c731ebfea37ac4dde28 (patch) | |
tree | 8dc8f770de4f578c0d9be905efaa99a05d3bc8d6 | |
parent | abe26c8d69d7bfa5400e52ad4bb96020df0cdf68 (diff) |
Fix hw layer overdraw/update visualization
Fixes: 28748255
Change-Id: I83b531cdf5e4407fd17edd72d96e6189924926fa
-rw-r--r-- | libs/hwui/Android.mk | 1 | ||||
-rw-r--r-- | libs/hwui/BakedOpDispatcher.cpp | 32 | ||||
-rw-r--r-- | libs/hwui/BakedOpRenderer.cpp | 1 | ||||
-rw-r--r-- | libs/hwui/FrameBuilder.cpp | 5 | ||||
-rw-r--r-- | libs/hwui/renderstate/OffscreenBufferPool.h | 2 | ||||
-rw-r--r-- | libs/hwui/tests/common/TestUtils.h | 22 | ||||
-rw-r--r-- | libs/hwui/tests/unit/BakedOpDispatcherTests.cpp | 91 | ||||
-rw-r--r-- | libs/hwui/tests/unit/TestUtilsTests.cpp | 36 |
8 files changed, 177 insertions, 13 deletions
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 8857c41dc33d..faf6a52bca85 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -259,6 +259,7 @@ LOCAL_SRC_FILES += \ tests/unit/SkiaBehaviorTests.cpp \ tests/unit/SnapshotTests.cpp \ tests/unit/StringUtilsTests.cpp \ + tests/unit/TestUtilsTests.cpp \ tests/unit/TextDropShadowCacheTests.cpp \ tests/unit/VectorDrawableTests.cpp diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp index 2ec7b75cb91d..59c106578cf3 100644 --- a/libs/hwui/BakedOpDispatcher.cpp +++ b/libs/hwui/BakedOpDispatcher.cpp @@ -791,6 +791,16 @@ void BakedOpDispatcher::onTextureLayerOp(BakedOpRenderer& renderer, const Textur renderer.renderGlop(state, glop); } +void renderRectForLayer(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state, + int color, SkXfermode::Mode mode, SkColorFilter* colorFilter) { + SkPaint paint; + paint.setColor(color); + paint.setXfermodeMode(mode); + paint.setColorFilter(colorFilter); + RectOp rectOp(op.unmappedBounds, op.localMatrix, op.localClip, &paint); + BakedOpDispatcher::onRectOp(renderer, rectOp, state); +} + void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) { // Note that we don't use op->paint in this function - it's never set on a LayerOp OffscreenBuffer* buffer = *op.layerHandle; @@ -798,12 +808,9 @@ void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, if (CC_UNLIKELY(!buffer)) { // Layer was not allocated, which can occur if there were no draw ops inside. We draw the // equivalent by drawing a rect with the same layer properties (alpha/xfer/filter). - SkPaint paint; - paint.setAlpha(op.alpha * 255); - paint.setXfermodeMode(op.mode); - paint.setColorFilter(op.colorFilter); - RectOp rectOp(op.unmappedBounds, op.localMatrix, op.localClip, &paint); - BakedOpDispatcher::onRectOp(renderer, rectOp, state); + int color = SkColorSetA(SK_ColorTRANSPARENT, op.alpha * 255); + renderRectForLayer(renderer, op, state, + color, op.mode, op.colorFilter); } else { float layerAlpha = op.alpha * state.alpha; Glop glop; @@ -817,6 +824,19 @@ void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, .build(); renderer.renderGlop(state, glop); } + + if (buffer && !buffer->hasRenderedSinceRepaint) { + buffer->hasRenderedSinceRepaint = true; + if (CC_UNLIKELY(Properties::debugLayersUpdates)) { + // render debug layer highlight + renderRectForLayer(renderer, op, state, + 0x7f00ff00, SkXfermode::Mode::kSrcOver_Mode, nullptr); + } else if (CC_UNLIKELY(Properties::debugOverdraw)) { + // render transparent to increment overdraw for repaint area + renderRectForLayer(renderer, op, state, + SK_ColorTRANSPARENT, SkXfermode::Mode::kSrcOver_Mode, nullptr); + } + } } void BakedOpDispatcher::onCopyToLayerOp(BakedOpRenderer& renderer, const CopyToLayerOp& op, const BakedOpState& state) { diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp index 3c302b3f6c9f..eed012636bb4 100644 --- a/libs/hwui/BakedOpRenderer.cpp +++ b/libs/hwui/BakedOpRenderer.cpp @@ -55,6 +55,7 @@ void BakedOpRenderer::startRepaintLayer(OffscreenBuffer* offscreenBuffer, const } mRenderTarget.offscreenBuffer = offscreenBuffer; + mRenderTarget.offscreenBuffer->hasRenderedSinceRepaint = false; // create and bind framebuffer mRenderTarget.frameBufferId = mRenderState.createFramebuffer(); diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp index 84c8a745ad1e..746e99a96123 100644 --- a/libs/hwui/FrameBuilder.cpp +++ b/libs/hwui/FrameBuilder.cpp @@ -81,7 +81,8 @@ void FrameBuilder::deferLayers(const LayerUpdateQueue& layers) { // only schedule repaint if node still on layer - possible it may have been // removed during a dropped frame, but layers may still remain scheduled so // as not to lose info on what portion is damaged - if (CC_LIKELY(layerNode->getLayer() != nullptr)) { + OffscreenBuffer* layer = layerNode->getLayer(); + if (CC_LIKELY(layer)) { ATRACE_FORMAT("Optimize HW Layer DisplayList %s %ux%u", layerNode->getName(), layerNode->getWidth(), layerNode->getHeight()); @@ -90,7 +91,7 @@ void FrameBuilder::deferLayers(const LayerUpdateQueue& layers) { // map current light center into RenderNode's coordinate space Vector3 lightCenter = mCanvasState.currentSnapshot()->getRelativeLightCenter(); - layerNode->getLayer()->inverseTransformInWindow.mapPoint3d(lightCenter); + layer->inverseTransformInWindow.mapPoint3d(lightCenter); saveForLayer(layerNode->getWidth(), layerNode->getHeight(), 0, 0, layerDamage, lightCenter, nullptr, layerNode); diff --git a/libs/hwui/renderstate/OffscreenBufferPool.h b/libs/hwui/renderstate/OffscreenBufferPool.h index 089f131668a0..73a3392a7811 100644 --- a/libs/hwui/renderstate/OffscreenBufferPool.h +++ b/libs/hwui/renderstate/OffscreenBufferPool.h @@ -77,6 +77,8 @@ public: // vbo / size of mesh GLsizei elementCount = 0; GLuint vbo = 0; + + bool hasRenderedSinceRepaint; }; /** diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index dbaefa470b50..4536bef6d391 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -74,6 +74,28 @@ typedef DisplayListCanvas TestCanvas; }; \ void test_case_name##_##test_name##_RenderThreadTest::doTheThing(renderthread::RenderThread& renderThread) +/** + * Sets a property value temporarily, generally for the duration of a test, restoring the previous + * value when going out of scope. + * + * Can be used e.g. to test behavior only active while Properties::debugOverdraw is enabled. + */ +template <typename T> +class ScopedProperty { +public: + ScopedProperty(T& property, T newValue) + : mPropertyPtr(&property) + , mOldValue(property) { + property = newValue; + } + ~ScopedProperty() { + *mPropertyPtr = mOldValue; + } +private: + T* mPropertyPtr; + T mOldValue; +}; + class TestUtils { public: class SignalingDtor { diff --git a/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp b/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp index 5613f9fd729c..01d3d7066ae3 100644 --- a/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp +++ b/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp @@ -16,21 +16,22 @@ #include <gtest/gtest.h> -#include <RecordedOp.h> #include <BakedOpDispatcher.h> #include <BakedOpRenderer.h> #include <FrameBuilder.h> -#include <SkBlurDrawLooper.h> +#include <LayerUpdateQueue.h> #include <hwui/Paint.h> +#include <RecordedOp.h> #include <tests/common/TestUtils.h> +#include <utils/Color.h> +#include <SkBlurDrawLooper.h> #include <SkDashPathEffect.h> using namespace android::uirenderer; static BakedOpRenderer::LightInfo sLightInfo; const FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50}; -static Rect sBaseClip(100, 100); class ValidatingBakedOpRenderer : public BakedOpRenderer { public: @@ -55,7 +56,7 @@ static void testUnmergedGlopDispatch(renderthread::RenderThread& renderThread, R std::function<void(const Glop& glop)> glopVerifier) { // Create op, and wrap with basic state. LinearAllocator allocator; - auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), sBaseClip); + auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 100)); auto state = BakedOpState::tryConstruct(allocator, *snapshot, *op); ASSERT_NE(nullptr, state); @@ -194,4 +195,84 @@ RENDERTHREAD_TEST(BakedOpDispatcher, renderTextWithShadow) { frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer); ASSERT_EQ(3, glopCount) << "Exactly three glops expected"; -}
\ No newline at end of file +} + +static void validateLayerDraw(renderthread::RenderThread& renderThread, + std::function<void(const Glop& glop)> validator) { + auto node = TestUtils::createNode(0, 0, 100, 100, + [](RenderProperties& props, TestCanvas& canvas) { + props.mutateLayerProperties().setType(LayerType::RenderLayer); + + // provide different blend mode, so decoration draws contrast + props.mutateLayerProperties().setXferMode(SkXfermode::Mode::kSrc_Mode); + canvas.drawColor(Color::Black, SkXfermode::Mode::kSrcOver_Mode); + }); + OffscreenBuffer** layerHandle = node->getLayerHandle(); + + auto syncedNode = TestUtils::getSyncedNode(node); + + // create RenderNode's layer here in same way prepareTree would + OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100); + *layerHandle = &layer; + { + LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid + layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(0, 0, 100, 100)); + + ValidatingBakedOpRenderer renderer(renderThread.renderState(), validator); + FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferLayers(layerUpdateQueue); + frameBuilder.deferRenderNode(*syncedNode); + frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer); + } + + // clean up layer pointer, so we can safely destruct RenderNode + *layerHandle = nullptr; +} + +static FloatColor makeFloatColor(uint32_t color) { + FloatColor c; + c.set(color); + return c; +} + +RENDERTHREAD_TEST(BakedOpDispatcher, layerUpdateProperties) { + for (bool debugOverdraw : { false, true }) { + for (bool debugLayersUpdates : { false, true }) { + ScopedProperty<bool> ovdProp(Properties::debugOverdraw, debugOverdraw); + ScopedProperty<bool> lupProp(Properties::debugLayersUpdates, debugLayersUpdates); + + int glopCount = 0; + validateLayerDraw(renderThread, [&glopCount, &debugLayersUpdates](const Glop& glop) { + if (glopCount == 0) { + // 0 - Black layer fill + EXPECT_TRUE(glop.fill.colorEnabled); + EXPECT_EQ(makeFloatColor(Color::Black), glop.fill.color); + } else if (glopCount == 1) { + // 1 - Uncolored (textured) layer draw + EXPECT_FALSE(glop.fill.colorEnabled); + } else if (glopCount == 2) { + // 2 - layer overlay, if present + EXPECT_TRUE(glop.fill.colorEnabled); + // blend srcover, different from that of layer + EXPECT_EQ(GLenum(GL_ONE), glop.blend.src); + EXPECT_EQ(GLenum(GL_ONE_MINUS_SRC_ALPHA), glop.blend.dst); + EXPECT_EQ(makeFloatColor(debugLayersUpdates ? 0x7f00ff00 : 0), + glop.fill.color) << "Should be transparent green if debugLayersUpdates"; + } else if (glopCount < 7) { + // 3 - 6 - overdraw indicator overlays, if present + EXPECT_TRUE(glop.fill.colorEnabled); + uint32_t expectedColor = Caches::getInstance().getOverdrawColor(glopCount - 2); + ASSERT_EQ(makeFloatColor(expectedColor), glop.fill.color); + } else { + ADD_FAILURE() << "Too many glops observed"; + } + glopCount++; + }); + int expectedCount = 2; + if (debugLayersUpdates || debugOverdraw) expectedCount++; + if (debugOverdraw) expectedCount += 4; + EXPECT_EQ(expectedCount, glopCount); + } + } +} diff --git a/libs/hwui/tests/unit/TestUtilsTests.cpp b/libs/hwui/tests/unit/TestUtilsTests.cpp new file mode 100644 index 000000000000..490595773724 --- /dev/null +++ b/libs/hwui/tests/unit/TestUtilsTests.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2016 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 "tests/common/TestUtils.h" +#include "Properties.h" + +#include <gtest/gtest.h> + +using namespace android::uirenderer; + +TEST(ScopedProperty, simpleBool) { + bool previous = Properties::debugOverdraw; + { + ScopedProperty<bool> debugOverdraw(Properties::debugOverdraw, true); + EXPECT_TRUE(Properties::debugOverdraw); + } + EXPECT_EQ(previous, Properties::debugOverdraw); + { + ScopedProperty<bool> debugOverdraw(Properties::debugOverdraw, false); + EXPECT_FALSE(Properties::debugOverdraw); + } + EXPECT_EQ(previous, Properties::debugOverdraw); +} |