Merge "Fix hw layer overdraw/update visualization" into nyc-dev
am: 189e3e5ec5
* commit '189e3e5ec5b698f1062fb98692fa75d3750148e0':
Fix hw layer overdraw/update visualization
Change-Id: Ic4c49b25e0d1709d34974adb828c3ab4644415e5
This commit is contained in:
@@ -258,6 +258,7 @@ LOCAL_SRC_FILES += \
|
|||||||
tests/unit/SkiaBehaviorTests.cpp \
|
tests/unit/SkiaBehaviorTests.cpp \
|
||||||
tests/unit/SnapshotTests.cpp \
|
tests/unit/SnapshotTests.cpp \
|
||||||
tests/unit/StringUtilsTests.cpp \
|
tests/unit/StringUtilsTests.cpp \
|
||||||
|
tests/unit/TestUtilsTests.cpp \
|
||||||
tests/unit/TextDropShadowCacheTests.cpp \
|
tests/unit/TextDropShadowCacheTests.cpp \
|
||||||
tests/unit/VectorDrawableTests.cpp
|
tests/unit/VectorDrawableTests.cpp
|
||||||
|
|
||||||
|
|||||||
@@ -791,6 +791,16 @@ void BakedOpDispatcher::onTextureLayerOp(BakedOpRenderer& renderer, const Textur
|
|||||||
renderer.renderGlop(state, glop);
|
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) {
|
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
|
// Note that we don't use op->paint in this function - it's never set on a LayerOp
|
||||||
OffscreenBuffer* buffer = *op.layerHandle;
|
OffscreenBuffer* buffer = *op.layerHandle;
|
||||||
@@ -798,12 +808,9 @@ void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op,
|
|||||||
if (CC_UNLIKELY(!buffer)) {
|
if (CC_UNLIKELY(!buffer)) {
|
||||||
// Layer was not allocated, which can occur if there were no draw ops inside. We draw the
|
// 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).
|
// equivalent by drawing a rect with the same layer properties (alpha/xfer/filter).
|
||||||
SkPaint paint;
|
int color = SkColorSetA(SK_ColorTRANSPARENT, op.alpha * 255);
|
||||||
paint.setAlpha(op.alpha * 255);
|
renderRectForLayer(renderer, op, state,
|
||||||
paint.setXfermodeMode(op.mode);
|
color, op.mode, op.colorFilter);
|
||||||
paint.setColorFilter(op.colorFilter);
|
|
||||||
RectOp rectOp(op.unmappedBounds, op.localMatrix, op.localClip, &paint);
|
|
||||||
BakedOpDispatcher::onRectOp(renderer, rectOp, state);
|
|
||||||
} else {
|
} else {
|
||||||
float layerAlpha = op.alpha * state.alpha;
|
float layerAlpha = op.alpha * state.alpha;
|
||||||
Glop glop;
|
Glop glop;
|
||||||
@@ -817,6 +824,19 @@ void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op,
|
|||||||
.build();
|
.build();
|
||||||
renderer.renderGlop(state, glop);
|
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) {
|
void BakedOpDispatcher::onCopyToLayerOp(BakedOpRenderer& renderer, const CopyToLayerOp& op, const BakedOpState& state) {
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ void BakedOpRenderer::startRepaintLayer(OffscreenBuffer* offscreenBuffer, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
mRenderTarget.offscreenBuffer = offscreenBuffer;
|
mRenderTarget.offscreenBuffer = offscreenBuffer;
|
||||||
|
mRenderTarget.offscreenBuffer->hasRenderedSinceRepaint = false;
|
||||||
|
|
||||||
// create and bind framebuffer
|
// create and bind framebuffer
|
||||||
mRenderTarget.frameBufferId = mRenderState.createFramebuffer();
|
mRenderTarget.frameBufferId = mRenderState.createFramebuffer();
|
||||||
|
|||||||
@@ -81,7 +81,8 @@ void FrameBuilder::deferLayers(const LayerUpdateQueue& layers) {
|
|||||||
// only schedule repaint if node still on layer - possible it may have been
|
// 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
|
// removed during a dropped frame, but layers may still remain scheduled so
|
||||||
// as not to lose info on what portion is damaged
|
// 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",
|
ATRACE_FORMAT("Optimize HW Layer DisplayList %s %ux%u",
|
||||||
layerNode->getName(), layerNode->getWidth(), layerNode->getHeight());
|
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
|
// map current light center into RenderNode's coordinate space
|
||||||
Vector3 lightCenter = mCanvasState.currentSnapshot()->getRelativeLightCenter();
|
Vector3 lightCenter = mCanvasState.currentSnapshot()->getRelativeLightCenter();
|
||||||
layerNode->getLayer()->inverseTransformInWindow.mapPoint3d(lightCenter);
|
layer->inverseTransformInWindow.mapPoint3d(lightCenter);
|
||||||
|
|
||||||
saveForLayer(layerNode->getWidth(), layerNode->getHeight(), 0, 0,
|
saveForLayer(layerNode->getWidth(), layerNode->getHeight(), 0, 0,
|
||||||
layerDamage, lightCenter, nullptr, layerNode);
|
layerDamage, lightCenter, nullptr, layerNode);
|
||||||
|
|||||||
@@ -77,6 +77,8 @@ public:
|
|||||||
// vbo / size of mesh
|
// vbo / size of mesh
|
||||||
GLsizei elementCount = 0;
|
GLsizei elementCount = 0;
|
||||||
GLuint vbo = 0;
|
GLuint vbo = 0;
|
||||||
|
|
||||||
|
bool hasRenderedSinceRepaint;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -74,6 +74,28 @@ typedef DisplayListCanvas TestCanvas;
|
|||||||
}; \
|
}; \
|
||||||
void test_case_name##_##test_name##_RenderThreadTest::doTheThing(renderthread::RenderThread& renderThread)
|
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 {
|
class TestUtils {
|
||||||
public:
|
public:
|
||||||
class SignalingDtor {
|
class SignalingDtor {
|
||||||
|
|||||||
@@ -16,21 +16,22 @@
|
|||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
#include <RecordedOp.h>
|
|
||||||
#include <BakedOpDispatcher.h>
|
#include <BakedOpDispatcher.h>
|
||||||
#include <BakedOpRenderer.h>
|
#include <BakedOpRenderer.h>
|
||||||
#include <FrameBuilder.h>
|
#include <FrameBuilder.h>
|
||||||
#include <SkBlurDrawLooper.h>
|
#include <LayerUpdateQueue.h>
|
||||||
#include <hwui/Paint.h>
|
#include <hwui/Paint.h>
|
||||||
|
#include <RecordedOp.h>
|
||||||
#include <tests/common/TestUtils.h>
|
#include <tests/common/TestUtils.h>
|
||||||
|
#include <utils/Color.h>
|
||||||
|
|
||||||
|
#include <SkBlurDrawLooper.h>
|
||||||
#include <SkDashPathEffect.h>
|
#include <SkDashPathEffect.h>
|
||||||
|
|
||||||
using namespace android::uirenderer;
|
using namespace android::uirenderer;
|
||||||
|
|
||||||
static BakedOpRenderer::LightInfo sLightInfo;
|
static BakedOpRenderer::LightInfo sLightInfo;
|
||||||
const FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50};
|
const FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50};
|
||||||
static Rect sBaseClip(100, 100);
|
|
||||||
|
|
||||||
class ValidatingBakedOpRenderer : public BakedOpRenderer {
|
class ValidatingBakedOpRenderer : public BakedOpRenderer {
|
||||||
public:
|
public:
|
||||||
@@ -55,7 +56,7 @@ static void testUnmergedGlopDispatch(renderthread::RenderThread& renderThread, R
|
|||||||
std::function<void(const Glop& glop)> glopVerifier) {
|
std::function<void(const Glop& glop)> glopVerifier) {
|
||||||
// Create op, and wrap with basic state.
|
// Create op, and wrap with basic state.
|
||||||
LinearAllocator allocator;
|
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);
|
auto state = BakedOpState::tryConstruct(allocator, *snapshot, *op);
|
||||||
ASSERT_NE(nullptr, state);
|
ASSERT_NE(nullptr, state);
|
||||||
|
|
||||||
@@ -194,4 +195,84 @@ RENDERTHREAD_TEST(BakedOpDispatcher, renderTextWithShadow) {
|
|||||||
|
|
||||||
frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
|
frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
|
||||||
ASSERT_EQ(3, glopCount) << "Exactly three glops expected";
|
ASSERT_EQ(3, glopCount) << "Exactly three glops expected";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
36
libs/hwui/tests/unit/TestUtilsTests.cpp
Normal file
36
libs/hwui/tests/unit/TestUtilsTests.cpp
Normal file
@@ -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);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user