Initial HW layer support in new reorderer/renderer

Shares vast majority of clipped savelayer code, with only minor
differences in lifecycle.

Doesn't yet handle fill region, resize, or window transform.

Change-Id: Iabdd71811590d2b937eb11e1b01ce556ade54a5a
This commit is contained in:
Chris Craik
2015-10-28 16:50:44 -07:00
parent 8462bad554
commit 0b7e8245db
21 changed files with 671 additions and 98 deletions

View File

@@ -56,6 +56,7 @@ hwui_src_files := \
Layer.cpp \
LayerCache.cpp \
LayerRenderer.cpp \
LayerUpdateQueue.cpp \
Matrix.cpp \
OpenGLRenderer.cpp \
Patch.cpp \
@@ -204,6 +205,7 @@ LOCAL_SRC_FILES += \
unit_tests/ClipAreaTests.cpp \
unit_tests/DamageAccumulatorTests.cpp \
unit_tests/FatVectorTests.cpp \
unit_tests/LayerUpdateQueueTests.cpp \
unit_tests/LinearAllocatorTests.cpp \
unit_tests/StringUtilsTests.cpp

View File

@@ -31,7 +31,9 @@ namespace uirenderer {
OffscreenBuffer::OffscreenBuffer(Caches& caches, uint32_t textureWidth, uint32_t textureHeight,
uint32_t viewportWidth, uint32_t viewportHeight)
: texture(caches)
: viewportWidth(viewportWidth)
, viewportHeight(viewportHeight)
, texture(caches)
, texCoords(0, viewportHeight / float(textureHeight), viewportWidth / float(textureWidth), 0) {
texture.width = textureWidth;
texture.height = textureHeight;
@@ -52,12 +54,29 @@ OffscreenBuffer::OffscreenBuffer(Caches& caches, uint32_t textureWidth, uint32_t
// BakedOpRenderer
////////////////////////////////////////////////////////////////////////////////
OffscreenBuffer* BakedOpRenderer::startLayer(uint32_t width, uint32_t height) {
OffscreenBuffer* BakedOpRenderer::createOffscreenBuffer(uint32_t width, uint32_t height) {
// TODO: get from cache!
return new OffscreenBuffer(Caches::getInstance(), width, height, width, height);
}
void BakedOpRenderer::destroyOffscreenBuffer(OffscreenBuffer* offscreenBuffer) {
// destroy and delete, since each clipped saveLayer is only drawn once.
offscreenBuffer->texture.deleteTexture();
// TODO: return texture/offscreenbuffer to cache!
delete offscreenBuffer;
}
OffscreenBuffer* BakedOpRenderer::createLayer(uint32_t width, uint32_t height) {
LOG_ALWAYS_FATAL_IF(mRenderTarget.offscreenBuffer, "already has layer...");
// TODO: really should be caching these!
OffscreenBuffer* buffer = new OffscreenBuffer(mCaches, width, height, width, height);
mRenderTarget.offscreenBuffer = buffer;
OffscreenBuffer* buffer = createOffscreenBuffer(width, height);
startLayer(buffer);
return buffer;
}
void BakedOpRenderer::startLayer(OffscreenBuffer* offscreenBuffer) {
mRenderTarget.offscreenBuffer = offscreenBuffer;
// create and bind framebuffer
mRenderTarget.frameBufferId = mRenderState.genFramebuffer();
@@ -65,7 +84,7 @@ OffscreenBuffer* BakedOpRenderer::startLayer(uint32_t width, uint32_t height) {
// attach the texture to the FBO
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
buffer->texture.id, 0);
offscreenBuffer->texture.id, 0);
LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "startLayer FAILED");
LOG_ALWAYS_FATAL_IF(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE,
"framebuffer incomplete!");
@@ -75,8 +94,7 @@ OffscreenBuffer* BakedOpRenderer::startLayer(uint32_t width, uint32_t height) {
glClear(GL_COLOR_BUFFER_BIT);
// Change the viewport & ortho projection
setViewport(width, height);
return buffer;
setViewport(offscreenBuffer->viewportWidth, offscreenBuffer->viewportHeight);
}
void BakedOpRenderer::endLayer() {
@@ -212,23 +230,21 @@ void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op,
// TODO: extend this to handle HW layers & paint properties which
// reside in node.properties().layerProperties()
float layerAlpha = (op.paint->getAlpha() / 255.0f) * state.alpha;
float layerAlpha = op.alpha * state.alpha;
const bool tryToSnap = state.computedState.transform.isPureTranslate();
Glop glop;
GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
.setRoundRectClipState(state.roundRectClipState)
.setMeshTexturedUvQuad(nullptr, buffer->texCoords)
.setFillLayer(buffer->texture, op.paint->getColorFilter(), layerAlpha, PaintUtils::getXfermodeDirect(op.paint), Blend::ModeOrderSwap::NoSwap)
.setFillLayer(buffer->texture, op.colorFilter, layerAlpha, op.mode, Blend::ModeOrderSwap::NoSwap)
.setTransform(state.computedState.transform, TransformFlags::None)
.setModelViewMapUnitToRectOptionalSnap(tryToSnap, op.unmappedBounds)
.build();
renderer.renderGlop(state, glop);
// destroy and delete, since each clipped saveLayer is only drawn once.
buffer->texture.deleteTexture();
// TODO: return texture/offscreenbuffer to cache!
delete buffer;
if (op.destroy) {
BakedOpRenderer::destroyOffscreenBuffer(buffer);
}
}
} // namespace uirenderer

View File

@@ -37,7 +37,8 @@ class OffscreenBuffer {
public:
OffscreenBuffer(Caches& caches, uint32_t textureWidth, uint32_t textureHeight,
uint32_t viewportWidth, uint32_t viewportHeight);
uint32_t viewportWidth;
uint32_t viewportHeight;
Texture texture;
Rect texCoords;
Region region;
@@ -60,12 +61,16 @@ public:
, mOpaque(opaque) {
}
static OffscreenBuffer* createOffscreenBuffer(uint32_t width, uint32_t height);
static void destroyOffscreenBuffer(OffscreenBuffer*);
RenderState& renderState() { return mRenderState; }
Caches& caches() { return mCaches; }
void startFrame(uint32_t width, uint32_t height);
void endFrame();
OffscreenBuffer* startLayer(uint32_t width, uint32_t height);
OffscreenBuffer* createLayer(uint32_t width, uint32_t height);
void startLayer(OffscreenBuffer* offscreenBuffer);
void endLayer();
Texture* getTexture(const SkBitmap* bitmap);

View File

@@ -154,7 +154,11 @@ public:
return allocator.usedSize();
}
bool isEmpty() {
#if HWUI_NEW_OPS
return ops.empty();
#else
return !hasDrawOps;
#endif
}
private:
@@ -179,7 +183,7 @@ private:
// List of functors
LsaVector<Functor*> functors;
bool hasDrawOps;
bool hasDrawOps; // only used if !HWUI_NEW_OPS
void cleanupResources();
};

View File

@@ -0,0 +1,42 @@
/*
* 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 "LayerUpdateQueue.h"
#include "RenderNode.h"
namespace android {
namespace uirenderer {
void LayerUpdateQueue::clear() {
mEntries.clear();
}
void LayerUpdateQueue::enqueueLayerWithDamage(RenderNode* renderNode, Rect damage) {
damage.doIntersect(0, 0, renderNode->getWidth(), renderNode->getHeight());
if (!damage.isEmpty()) {
for (Entry& entry : mEntries) {
if (CC_UNLIKELY(entry.renderNode == renderNode)) {
entry.damage.unionWith(damage);
return;
}
}
mEntries.emplace_back(renderNode, damage);
}
}
} // namespace uirenderer
} // namespace android

View File

@@ -0,0 +1,53 @@
/*
* 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.
*/
#ifndef ANDROID_HWUI_LAYER_UPDATE_QUEUE_H
#define ANDROID_HWUI_LAYER_UPDATE_QUEUE_H
#include "Rect.h"
#include "utils/Macros.h"
#include <vector>
#include <unordered_map>
namespace android {
namespace uirenderer {
class RenderNode;
class LayerUpdateQueue {
PREVENT_COPY_AND_ASSIGN(LayerUpdateQueue);
public:
struct Entry {
Entry(RenderNode* renderNode, const Rect& damage)
: renderNode(renderNode)
, damage(damage) {}
RenderNode* renderNode;
Rect damage;
};
LayerUpdateQueue() {}
void enqueueLayerWithDamage(RenderNode* renderNode, Rect dirty);
void clear();
const std::vector<Entry> entries() const { return mEntries; }
private:
std::vector<Entry> mEntries;
};
}; // namespace uirenderer
}; // namespace android
#endif // ANDROID_HWUI_LAYER_UPDATE_QUEUE_H

View File

@@ -18,6 +18,7 @@
#include "utils/PaintUtils.h"
#include "RenderNode.h"
#include "LayerUpdateQueue.h"
#include "SkCanvas.h"
#include "utils/Trace.h"
@@ -202,6 +203,14 @@ private:
Rect mClipRect;
};
OpReorderer::LayerReorderer::LayerReorderer(uint32_t width, uint32_t height,
const BeginLayerOp* beginLayerOp, RenderNode* renderNode)
: width(width)
, height(height)
, offscreenBuffer(renderNode ? renderNode->getLayer() : nullptr)
, beginLayerOp(beginLayerOp)
, renderNode(renderNode) {}
// iterate back toward target to see if anything drawn since should overlap the new op
// if no target, merging ops still iterate to find similar batch to insert after
void OpReorderer::LayerReorderer::locateInsertIndex(int batchId, const Rect& clippedBounds,
@@ -288,33 +297,48 @@ void OpReorderer::LayerReorderer::replayBakedOpsImpl(void* arg, BakedOpDispatche
}
void OpReorderer::LayerReorderer::dump() const {
ALOGD("LayerReorderer %p, %ux%u buffer %p, blo %p, rn %p",
this, width, height, offscreenBuffer, beginLayerOp, renderNode);
for (const BatchBase* batch : mBatches) {
batch->dump();
}
}
OpReorderer::OpReorderer(const SkRect& clip, uint32_t viewportWidth, uint32_t viewportHeight,
OpReorderer::OpReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
uint32_t viewportWidth, uint32_t viewportHeight,
const std::vector< sp<RenderNode> >& nodes)
: mCanvasState(*this) {
ATRACE_NAME("prepare drawing commands");
mLayerReorderers.emplace_back(viewportWidth, viewportHeight);
mLayerStack.push_back(0);
mLayerStack.push_back(0);
mCanvasState.initializeSaveStack(viewportWidth, viewportHeight,
clip.fLeft, clip.fTop, clip.fRight, clip.fBottom,
Vector3());
// Render all layers to be updated, in order. Defer in reverse order, so that they'll be
// updated in the order they're passed in (mLayerReorderers are issued to Renderer in reverse)
for (int i = layers.entries().size() - 1; i >= 0; i--) {
RenderNode* layerNode = layers.entries()[i].renderNode;
const Rect& layerDamage = layers.entries()[i].damage;
saveForLayer(layerNode->getWidth(), layerNode->getHeight(), nullptr, layerNode);
mCanvasState.writableSnapshot()->setClip(
layerDamage.left, layerDamage.top, layerDamage.right, layerDamage.bottom);
if (layerNode->getDisplayList()) {
deferImpl(*(layerNode->getDisplayList()));
}
restoreForLayer();
}
// Defer Fbo0
for (const sp<RenderNode>& node : nodes) {
if (node->nothingToDraw()) continue;
// TODO: dedupe this code with onRenderNode()
mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
if (node->applyViewProperties(mCanvasState)) {
// not rejected do ops...
const DisplayList& displayList = node->getDisplayList();
deferImpl(displayList);
}
mCanvasState.restore();
int count = mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
deferNodePropsAndOps(*node);
mCanvasState.restoreToCount(count);
}
}
@@ -334,6 +358,23 @@ void OpReorderer::onViewportInitialized() {}
void OpReorderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
void OpReorderer::deferNodePropsAndOps(RenderNode& node) {
if (node.applyViewProperties(mCanvasState)) {
// not rejected so render
if (node.getLayer()) {
// HW layer
LayerOp* drawLayerOp = new (mAllocator) LayerOp(node);
BakedOpState* bakedOpState = tryBakeOpState(*drawLayerOp);
if (bakedOpState) {
// Layer will be drawn into parent layer (which is now current, since we popped mLayerStack)
currentLayer().deferUnmergeableOp(mAllocator, bakedOpState, OpBatchType::Bitmap);
}
} else {
deferImpl(*(node.getDisplayList()));
}
}
}
/**
* Used to define a list of lambdas referencing private OpReorderer::onXXXXOp() methods.
*
@@ -365,11 +406,9 @@ void OpReorderer::onRenderNodeOp(const RenderNodeOp& op) {
mCanvasState.clipRect(op.localClipRect.left, op.localClipRect.top,
op.localClipRect.right, op.localClipRect.bottom, SkRegion::kIntersect_Op);
// apply RenderProperties state
if (op.renderNode->applyViewProperties(mCanvasState)) {
// if node not rejected based on properties, do ops...
deferImpl(op.renderNode->getDisplayList());
}
// then apply state from node properties, and defer ops
deferNodePropsAndOps(*op.renderNode);
mCanvasState.restoreToCount(count);
}
@@ -400,10 +439,8 @@ void OpReorderer::onSimpleRectsOp(const SimpleRectsOp& op) {
currentLayer().deferUnmergeableOp(mAllocator, bakedStateOp, OpBatchType::Vertices);
}
// TODO: test rejection at defer time, where the bounds become empty
void OpReorderer::onBeginLayerOp(const BeginLayerOp& op) {
const uint32_t layerWidth = (uint32_t) op.unmappedBounds.getWidth();
const uint32_t layerHeight = (uint32_t) op.unmappedBounds.getHeight();
void OpReorderer::saveForLayer(uint32_t layerWidth, uint32_t layerHeight,
const BeginLayerOp* beginLayerOp, RenderNode* renderNode) {
mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
mCanvasState.writableSnapshot()->transform->loadIdentity();
@@ -412,18 +449,27 @@ void OpReorderer::onBeginLayerOp(const BeginLayerOp& op) {
// create a new layer, and push its index on the stack
mLayerStack.push_back(mLayerReorderers.size());
mLayerReorderers.emplace_back(layerWidth, layerHeight);
mLayerReorderers.back().beginLayerOp = &op;
mLayerReorderers.emplace_back(layerWidth, layerHeight, beginLayerOp, renderNode);
}
void OpReorderer::restoreForLayer() {
// restore canvas, and pop finished layer off of the stack
mCanvasState.restore();
mLayerStack.pop_back();
}
// TODO: test rejection at defer time, where the bounds become empty
void OpReorderer::onBeginLayerOp(const BeginLayerOp& op) {
const uint32_t layerWidth = (uint32_t) op.unmappedBounds.getWidth();
const uint32_t layerHeight = (uint32_t) op.unmappedBounds.getHeight();
saveForLayer(layerWidth, layerHeight, &op, nullptr);
}
void OpReorderer::onEndLayerOp(const EndLayerOp& /* ignored */) {
mCanvasState.restore();
const BeginLayerOp& beginLayerOp = *currentLayer().beginLayerOp;
// pop finished layer off of the stack
int finishedLayerIndex = mLayerStack.back();
mLayerStack.pop_back();
restoreForLayer();
// record the draw operation into the previous layer's list of draw commands
// uses state from the associated beginLayerOp, since it has all the state needed for drawing

View File

@@ -32,6 +32,7 @@ namespace uirenderer {
class BakedOpState;
class BatchBase;
class LayerUpdateQueue;
class MergingOpBatch;
class OffscreenBuffer;
class OpBatch;
@@ -64,9 +65,14 @@ class OpReorderer : public CanvasStateClient {
*/
class LayerReorderer {
public:
// Create LayerReorderer for Fbo0
LayerReorderer(uint32_t width, uint32_t height)
: width(width)
, height(height) {}
: LayerReorderer(width, height, nullptr, nullptr) {};
// Create LayerReorderer for an offscreen layer, where beginLayerOp is present for a
// saveLayer, renderNode is present for a HW layer.
LayerReorderer(uint32_t width, uint32_t height,
const BeginLayerOp* beginLayerOp, RenderNode* renderNode);
// iterate back toward target to see if anything drawn since should overlap the new op
// if no target, merging ops still iterate to find similar batch to insert after
@@ -92,12 +98,12 @@ class OpReorderer : public CanvasStateClient {
void dump() const;
OffscreenBuffer* offscreenBuffer = nullptr;
const BeginLayerOp* beginLayerOp = nullptr;
const uint32_t width;
const uint32_t height;
OffscreenBuffer* offscreenBuffer;
const BeginLayerOp* beginLayerOp;
const RenderNode* renderNode;
private:
std::vector<BatchBase*> mBatches;
/**
@@ -112,8 +118,8 @@ class OpReorderer : public CanvasStateClient {
};
public:
// TODO: not final, just presented this way for simplicity. Layers too?
OpReorderer(const SkRect& clip, uint32_t viewportWidth, uint32_t viewportHeight,
OpReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
uint32_t viewportWidth, uint32_t viewportHeight,
const std::vector< sp<RenderNode> >& nodes);
OpReorderer(int viewportWidth, int viewportHeight, const DisplayList& displayList);
@@ -144,8 +150,13 @@ public:
// later in the list will be drawn by earlier ones
for (int i = mLayerReorderers.size() - 1; i >= 1; i--) {
LayerReorderer& layer = mLayerReorderers[i];
if (!layer.empty()) {
layer.offscreenBuffer = renderer.startLayer(layer.width, layer.height);
if (layer.renderNode) {
// cached HW layer - can't skip layer if empty
renderer.startLayer(layer.offscreenBuffer);
layer.replayBakedOpsImpl((void*)&renderer, receivers);
renderer.endLayer();
} else if (!layer.empty()) { // save layer - skip entire layer if empty
layer.offscreenBuffer = renderer.createLayer(layer.width, layer.height);
layer.replayBakedOpsImpl((void*)&renderer, receivers);
renderer.endLayer();
}
@@ -171,12 +182,19 @@ public:
virtual GLuint getTargetFbo() const override { return 0; }
private:
void saveForLayer(uint32_t layerWidth, uint32_t layerHeight,
const BeginLayerOp* beginLayerOp, RenderNode* renderNode);
void restoreForLayer();
LayerReorderer& currentLayer() { return mLayerReorderers[mLayerStack.back()]; }
BakedOpState* tryBakeOpState(const RecordedOp& recordedOp) {
return BakedOpState::tryConstruct(mAllocator, *mCanvasState.currentSnapshot(), recordedOp);
}
// should always be surrounded by a save/restore pair
void deferNodePropsAndOps(RenderNode& node);
void deferImpl(const DisplayList& displayList);
void replayBakedOpsImpl(void* arg, BakedOpDispatcher* receivers);

View File

@@ -20,6 +20,7 @@
#include "utils/LinearAllocator.h"
#include "Rect.h"
#include "Matrix.h"
#include "RenderNode.h"
#include "SkXfermode.h"
@@ -136,13 +137,42 @@ struct EndLayerOp : RecordedOp {
: RecordedOp(RecordedOpId::EndLayerOp, Rect(0, 0), Matrix4::identity(), Rect(0, 0), nullptr) {}
};
/**
* Draws an OffscreenBuffer.
*
* Alpha, mode, and colorfilter are embedded, since LayerOps are always dynamically generated,
* when creating/tracking a SkPaint* during defer isn't worth the bother.
*/
struct LayerOp : RecordedOp {
// Records a one-use (saveLayer) layer for drawing. Once drawn, the layer will be destroyed.
LayerOp(BASE_PARAMS, OffscreenBuffer** layerHandle)
: SUPER(LayerOp)
, layerHandle(layerHandle) {}
: SUPER_PAINTLESS(LayerOp)
, layerHandle(layerHandle)
, alpha(paint->getAlpha() / 255.0f)
, mode(PaintUtils::getXfermodeDirect(paint))
, colorFilter(paint->getColorFilter())
, destroy(true) {}
LayerOp(RenderNode& node)
: RecordedOp(RecordedOpId::LayerOp, Rect(node.getWidth(), node.getHeight()), Matrix4::identity(), Rect(node.getWidth(), node.getHeight()), nullptr)
, layerHandle(node.getLayerHandle())
, alpha(node.properties().layerProperties().alpha() / 255.0f)
, mode(node.properties().layerProperties().xferMode())
, colorFilter(node.properties().layerProperties().colorFilter())
, destroy(false) {}
// Records a handle to the Layer object, since the Layer itself won't be
// constructed until after this operation is constructed.
OffscreenBuffer** layerHandle;
const float alpha;
const SkXfermode::Mode mode;
// pointer to object owned by either LayerProperties, or a recorded Paint object in a
// BeginLayerOp. Lives longer than LayerOp in either case, so no skia ref counting is used.
SkColorFilter* colorFilter;
// whether to destroy the layer, once rendered
const bool destroy;
};
}; // namespace uirenderer

View File

@@ -77,7 +77,6 @@ SkCanvas* RecordingCanvas::asSkCanvas() {
// ----------------------------------------------------------------------------
void RecordingCanvas::onViewportInitialized() {
}
void RecordingCanvas::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {

View File

@@ -26,9 +26,10 @@
#include "SkiaCanvasProxy.h"
#include "Snapshot.h"
#include "SkDrawFilter.h"
#include "SkPaint.h"
#include "SkTLazy.h"
#include <SkDrawFilter.h>
#include <SkPaint.h>
#include <SkTLazy.h>
#include <vector>
namespace android {

View File

@@ -20,6 +20,7 @@
#include "Debug.h"
#if HWUI_NEW_OPS
#include "RecordedOp.h"
#include "BakedOpRenderer.h"
#endif
#include "DisplayListOp.h"
#include "LayerRenderer.h"
@@ -42,11 +43,15 @@ namespace android {
namespace uirenderer {
void RenderNode::debugDumpLayers(const char* prefix) {
#if HWUI_NEW_OPS
LOG_ALWAYS_FATAL("TODO: dump layer");
#else
if (mLayer) {
ALOGD("%sNode %p (%s) has layer %p (fbo = %u, wasBuildLayered = %s)",
prefix, this, getName(), mLayer, mLayer->getFbo(),
mLayer->wasBuildLayered ? "true" : "false");
}
#endif
if (mDisplayList) {
for (auto&& child : mDisplayList->getChildren()) {
child->renderNode->debugDumpLayers(prefix);
@@ -60,18 +65,21 @@ RenderNode::RenderNode()
, mDisplayList(nullptr)
, mStagingDisplayList(nullptr)
, mAnimatorManager(*this)
, mLayer(nullptr)
, mParentCount(0) {
}
RenderNode::~RenderNode() {
deleteDisplayList();
delete mStagingDisplayList;
#if HWUI_NEW_OPS
LOG_ALWAYS_FATAL_IF(mLayer, "layer missed detachment!");
#else
if (mLayer) {
ALOGW("Memory Warning: Layer %p missed its detachment, held on to for far too long!", mLayer);
mLayer->postDecStrong();
mLayer = nullptr;
}
#endif
}
void RenderNode::setStagingDisplayList(DisplayList* displayList) {
@@ -240,13 +248,29 @@ void RenderNode::prepareLayer(TreeInfo& info, uint32_t dirtyMask) {
}
}
layer_t* createLayer(RenderState& renderState, uint32_t width, uint32_t height) {
#if HWUI_NEW_OPS
return BakedOpRenderer::createOffscreenBuffer(width, height);
#else
return LayerRenderer::createRenderLayer(renderState, width, height);
#endif
}
void destroyLayer(layer_t* layer) {
#if HWUI_NEW_OPS
BakedOpRenderer::destroyOffscreenBuffer(layer);
#else
LayerRenderer::destroyLayer(layer);
#endif
}
void RenderNode::pushLayerUpdate(TreeInfo& info) {
LayerType layerType = properties().effectiveLayerType();
// If we are not a layer OR we cannot be rendered (eg, view was detached)
// we need to destroy any Layers we may have had previously
if (CC_LIKELY(layerType != LayerType::RenderLayer) || CC_UNLIKELY(!isRenderable())) {
if (CC_UNLIKELY(mLayer)) {
LayerRenderer::destroyLayer(mLayer);
destroyLayer(mLayer);
mLayer = nullptr;
}
return;
@@ -254,14 +278,18 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) {
bool transformUpdateNeeded = false;
if (!mLayer) {
mLayer = LayerRenderer::createRenderLayer(
info.canvasContext.getRenderState(), getWidth(), getHeight());
applyLayerPropertiesToLayer(info);
damageSelf(info);
transformUpdateNeeded = true;
mLayer = createLayer(info.canvasContext.getRenderState(), getWidth(), getHeight());
damageSelf(info);
transformUpdateNeeded = true;
#if HWUI_NEW_OPS
} else if (mLayer->viewportWidth != getWidth() || mLayer->viewportHeight != getHeight()) {
// TODO: allow it to grow larger
if (getWidth() > mLayer->texture.width || getHeight() > mLayer->texture.height) {
#else
} else if (mLayer->layer.getWidth() != getWidth() || mLayer->layer.getHeight() != getHeight()) {
if (!LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight())) {
LayerRenderer::destroyLayer(mLayer);
#endif
destroyLayer(mLayer);
mLayer = nullptr;
}
damageSelf(info);
@@ -276,7 +304,7 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) {
if (info.errorHandler) {
std::ostringstream err;
err << "Unable to create layer for " << getName();
const int maxTextureSize = Caches::getInstance().maxTextureSize;
const uint32_t maxTextureSize = Caches::getInstance().maxTextureSize;
if (getWidth() > maxTextureSize || getHeight() > maxTextureSize) {
err << ", size " << getWidth() << "x" << getHeight()
<< " exceeds max size " << maxTextureSize;
@@ -292,9 +320,16 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) {
// update the transform in window of the layer to reset its origin wrt light source position
Matrix4 windowTransform;
info.damageAccumulator->computeCurrentTransform(&windowTransform);
#if HWUI_NEW_OPS
// TODO: update layer transform (perhaps as part of enqueueLayerWithDamage)
#else
mLayer->setWindowTransform(windowTransform);
#endif
}
#if HWUI_NEW_OPS
info.layerUpdateQueue->enqueueLayerWithDamage(this, dirty);
#else
if (dirty.intersect(0, 0, getWidth(), getHeight())) {
dirty.roundOut(&dirty);
mLayer->updateDeferred(this, dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom);
@@ -304,6 +339,7 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) {
if (info.renderer && mLayer->deferredUpdateScheduled) {
info.renderer->pushLayerUpdate(mLayer);
}
#endif
// There might be prefetched layers that need to be accounted for.
// That might be us, so tell CanvasContext that this layer is in the
@@ -365,7 +401,9 @@ void RenderNode::pushStagingPropertiesChanges(TreeInfo& info) {
damageSelf(info);
info.damageAccumulator->popTransform();
syncProperties();
#if !HWUI_NEW_OPS
applyLayerPropertiesToLayer(info);
#endif
// We could try to be clever and only re-damage if the matrix changed.
// However, we don't need to worry about that. The cost of over-damaging
// here is only going to be a single additional map rect of this node
@@ -376,6 +414,7 @@ void RenderNode::pushStagingPropertiesChanges(TreeInfo& info) {
}
}
#if !HWUI_NEW_OPS
void RenderNode::applyLayerPropertiesToLayer(TreeInfo& info) {
if (CC_LIKELY(!mLayer)) return;
@@ -384,6 +423,7 @@ void RenderNode::applyLayerPropertiesToLayer(TreeInfo& info) {
mLayer->setColorFilter(props.colorFilter());
mLayer->setBlend(props.needsBlending());
}
#endif
void RenderNode::syncDisplayList() {
// Make sure we inc first so that we don't fluctuate between 0 and 1,
@@ -451,7 +491,7 @@ void RenderNode::prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayL
void RenderNode::destroyHardwareResources() {
if (mLayer) {
LayerRenderer::destroyLayer(mLayer);
destroyLayer(mLayer);
mLayer = nullptr;
}
if (mDisplayList) {
@@ -978,7 +1018,11 @@ void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
return;
}
#if HWUI_NEW_OPS
const bool drawLayer = false;
#else
const bool drawLayer = (mLayer && (&renderer != mLayer->renderer.get()));
#endif
// If we are updating the contents of mLayer, we don't want to apply any of
// the RenderNode's properties to this issueOperations pass. Those will all
// be applied when the layer is drawn, aka when this is true.

View File

@@ -44,13 +44,22 @@ namespace android {
namespace uirenderer {
class CanvasState;
class DisplayListOp;
class DisplayListCanvas;
class DisplayListOp;
class OpenGLRenderer;
class OpReorderer;
class Rect;
class Layer;
class SkiaShader;
#if HWUI_NEW_OPS
class OffscreenBuffer;
typedef OffscreenBuffer layer_t;
#else
class Layer;
typedef Layer layer_t;
#endif
class ClipRectOp;
class SaveLayerOp;
class SaveOp;
@@ -162,11 +171,11 @@ public:
return mStagingProperties;
}
int getWidth() {
uint32_t getWidth() {
return properties().getWidth();
}
int getHeight() {
uint32_t getHeight() {
return properties().getHeight();
}
@@ -193,9 +202,13 @@ public:
}
// Only call if RenderNode has DisplayList...
const DisplayList& getDisplayList() const {
return *mDisplayList;
const DisplayList* getDisplayList() const {
return mDisplayList;
}
#if HWUI_NEW_OPS
OffscreenBuffer* getLayer() const { return mLayer; }
OffscreenBuffer** getLayerHandle() { return &mLayer; } // ugh...
#endif
private:
typedef key_value_pair_t<float, DrawRenderNodeOp*> ZDrawRenderNodeOpPair;
@@ -262,7 +275,9 @@ private:
void pushStagingPropertiesChanges(TreeInfo& info);
void pushStagingDisplayListChanges(TreeInfo& info);
void prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayList* subtree);
#if !HWUI_NEW_OPS
void applyLayerPropertiesToLayer(TreeInfo& info);
#endif
void prepareLayer(TreeInfo& info, uint32_t dirtyMask);
void pushLayerUpdate(TreeInfo& info);
void deleteDisplayList();
@@ -287,7 +302,7 @@ private:
// Owned by RT. Lifecycle is managed by prepareTree(), with the exception
// being in ~RenderNode() which may happen on any thread.
Layer* mLayer;
layer_t* mLayer = nullptr;
/**
* Draw time state - these properties are only set and used during rendering

View File

@@ -16,11 +16,11 @@
#ifndef TREEINFO_H
#define TREEINFO_H
#include <string>
#include "utils/Macros.h"
#include <utils/Timers.h>
#include "utils/Macros.h"
#include <string>
namespace android {
namespace uirenderer {
@@ -30,6 +30,7 @@ class CanvasContext;
}
class DamageAccumulator;
class LayerUpdateQueue;
class OpenGLRenderer;
class RenderState;
@@ -75,9 +76,14 @@ public:
// Must not be null during actual usage
DamageAccumulator* damageAccumulator = nullptr;
#if HWUI_NEW_OPS
LayerUpdateQueue* layerUpdateQueue = nullptr;
#else
// The renderer that will be drawing the next frame. Use this to push any
// layer updates or similar. May be NULL.
OpenGLRenderer* renderer = nullptr;
#endif
ErrorHandler* errorHandler = nullptr;
struct Out {

View File

@@ -56,7 +56,9 @@ void BM_OpReorderer_defer::Run(int iters) {
BENCHMARK_NO_ARG(BM_OpReorderer_deferAndRender);
void BM_OpReorderer_deferAndRender::Run(int iters) {
TestUtils::runOnRenderThread([this, iters](RenderState& renderState, Caches& caches) {
TestUtils::runOnRenderThread([this, iters](renderthread::RenderThread& thread) {
RenderState& renderState = thread.renderState();
Caches& caches = Caches::getInstance();
StartBenchmarkTiming();
for (int i = 0; i < iters; i++) {
OpReorderer reorderer(200, 200, *sReorderingDisplayList);

View File

@@ -20,6 +20,7 @@
#include "Caches.h"
#include "DeferredLayerUpdater.h"
#include "EglManager.h"
#include "LayerUpdateQueue.h"
#include "LayerRenderer.h"
#include "OpenGLRenderer.h"
#include "Properties.h"
@@ -198,7 +199,11 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo,
mCurrentFrameInfo->markSyncStart();
info.damageAccumulator = &mDamageAccumulator;
#if HWUI_NEW_OPS
info.layerUpdateQueue = &mLayerUpdateQueue;
#else
info.renderer = mCanvas;
#endif
mAnimationContext->startFrame(info.mode);
for (const sp<RenderNode>& node : mRenderNodes) {
@@ -333,7 +338,8 @@ void CanvasContext::draw() {
mEglManager.damageFrame(frame, dirty);
#if HWUI_NEW_OPS
OpReorderer reorderer(dirty, frame.width(), frame.height(), mRenderNodes);
OpReorderer reorderer(mLayerUpdateQueue, dirty, frame.width(), frame.height(), mRenderNodes);
mLayerUpdateQueue.clear();
BakedOpRenderer renderer(Caches::getInstance(), mRenderThread.renderState(), mOpaque);
// TODO: profiler().draw(mCanvas);
reorderer.replayBakedOps<BakedOpDispatcher>(renderer);
@@ -552,7 +558,11 @@ void CanvasContext::buildLayer(RenderNode* node) {
TreeInfo info(TreeInfo::MODE_FULL, *this);
info.damageAccumulator = &mDamageAccumulator;
#if HWUI_NEW_OPS
info.layerUpdateQueue = &mLayerUpdateQueue;
#else
info.renderer = mCanvas;
#endif
info.runAnimations = false;
node->prepareTree(info);
SkRect ignore;

View File

@@ -18,9 +18,10 @@
#define CANVASCONTEXT_H_
#include "DamageAccumulator.h"
#include "IContextFactory.h"
#include "FrameInfo.h"
#include "FrameInfoVisualizer.h"
#include "IContextFactory.h"
#include "LayerUpdateQueue.h"
#include "RenderNode.h"
#include "utils/RingBuffer.h"
#include "renderthread/RenderTask.h"
@@ -83,7 +84,7 @@ public:
void draw();
void destroy();
// IFrameCallback, Chroreographer-driven frame callback entry point
// IFrameCallback, Choreographer-driven frame callback entry point
virtual void doFrame() override;
void prepareAndDraw(RenderNode* node);
@@ -118,7 +119,7 @@ public:
void addRenderNode(RenderNode* node, bool placeFront) {
int pos = placeFront ? 0 : static_cast<int>(mRenderNodes.size());
mRenderNodes.emplace( mRenderNodes.begin() + pos, node);
mRenderNodes.emplace(mRenderNodes.begin() + pos, node);
}
void removeRenderNode(RenderNode* node) {
@@ -166,6 +167,7 @@ private:
OpenGLRenderer* mCanvas = nullptr;
bool mHaveNewSurface = false;
DamageAccumulator mDamageAccumulator;
LayerUpdateQueue mLayerUpdateQueue;
std::unique_ptr<AnimationContext> mAnimationContext;
std::vector< sp<RenderNode> > mRenderNodes;

View File

@@ -24,6 +24,7 @@
#include <RenderNode.h>
#include <renderthread/RenderProxy.h>
#include <renderthread/RenderTask.h>
#include <unit_tests/TestUtils.h>
#include "Benchmark.h"
#include "TestContext.h"
@@ -401,3 +402,27 @@ static Benchmark _SaveLayer(BenchmarkInfo{
"Tests the clipped saveLayer codepath. Draws content into offscreen buffers and back again.",
TreeContentAnimation::run<SaveLayerAnimation>
});
class HwLayerAnimation : public TreeContentAnimation {
public:
sp<RenderNode> card = TestUtils::createNode<TestCanvas>(0, 0, 200, 200, [] (TestCanvas& canvas) {
canvas.drawColor(0xFF0000FF, SkXfermode::kSrcOver_Mode);
}, true);
void createContent(int width, int height, TestCanvas* canvas) override {
canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); // background
canvas->drawRenderNode(card.get());
}
void doFrame(int frameNr) override {
int curFrame = frameNr % 150;
card->mutateStagingProperties().setTranslationX(curFrame);
card->mutateStagingProperties().setTranslationY(curFrame);
card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
}
};
static Benchmark _HwLayer(BenchmarkInfo{
"hwlayer",
"A nested pair of nodes with LAYER_TYPE_HARDWARE set on each. "
"Tests the hardware layer codepath.",
TreeContentAnimation::run<HwLayerAnimation>
});

View File

@@ -0,0 +1,82 @@
/*
* 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 <gtest/gtest.h>
#include <LayerUpdateQueue.h>
#include <RenderNode.h>
#include <unit_tests/TestUtils.h>
namespace android {
namespace uirenderer {
TEST(LayerUpdateQueue, construct) {
LayerUpdateQueue queue;
EXPECT_TRUE(queue.entries().empty());
}
// sync node properties, so properties() reflects correct width and height
static sp<RenderNode> createSyncedNode(uint32_t width, uint32_t height) {
sp<RenderNode> node = TestUtils::createNode(0, 0, width, height);
TestUtils::syncNodePropertiesAndDisplayList(node);
return node;
}
TEST(LayerUpdateQueue, enqueueSimple) {
sp<RenderNode> a = createSyncedNode(100, 100);
sp<RenderNode> b = createSyncedNode(200, 200);
LayerUpdateQueue queue;
queue.enqueueLayerWithDamage(a.get(), Rect(25, 25, 75, 75));
queue.enqueueLayerWithDamage(b.get(), Rect(100, 100, 300, 300));
EXPECT_EQ(2u, queue.entries().size());
EXPECT_EQ(a.get(), queue.entries()[0].renderNode);
EXPECT_EQ(Rect(25, 25, 75, 75), queue.entries()[0].damage);
EXPECT_EQ(b.get(), queue.entries()[1].renderNode);
EXPECT_EQ(Rect(100, 100, 200, 200), queue.entries()[1].damage); // clipped to bounds
}
TEST(LayerUpdateQueue, enqueueUnion) {
sp<RenderNode> a = createSyncedNode(100, 100);
LayerUpdateQueue queue;
queue.enqueueLayerWithDamage(a.get(), Rect(10, 10, 20, 20));
queue.enqueueLayerWithDamage(a.get(), Rect(30, 30, 40, 40));
EXPECT_EQ(1u, queue.entries().size());
EXPECT_EQ(a.get(), queue.entries()[0].renderNode);
EXPECT_EQ(Rect(10, 10, 40, 40), queue.entries()[0].damage);
}
TEST(LayerUpdateQueue, clear) {
sp<RenderNode> a = createSyncedNode(100, 100);
LayerUpdateQueue queue;
queue.enqueueLayerWithDamage(a.get(), Rect(100, 100));
EXPECT_FALSE(queue.entries().empty());
queue.clear();
EXPECT_TRUE(queue.entries().empty());
}
};
};

View File

@@ -20,6 +20,7 @@
#include <OpReorderer.h>
#include <RecordedOp.h>
#include <RecordingCanvas.h>
#include <renderthread/CanvasContext.h> // todo: remove
#include <unit_tests/TestUtils.h>
#include <unordered_map>
@@ -27,6 +28,7 @@
namespace android {
namespace uirenderer {
LayerUpdateQueue sEmptyLayerUpdateQueue;
/**
* Virtual class implemented by each test to redirect static operation / state transitions to
@@ -42,7 +44,8 @@ namespace uirenderer {
class TestRendererBase {
public:
virtual ~TestRendererBase() {}
virtual OffscreenBuffer* startLayer(uint32_t width, uint32_t height) { ADD_FAILURE(); return nullptr; }
virtual OffscreenBuffer* createLayer(uint32_t, uint32_t) { ADD_FAILURE(); return nullptr; }
virtual void startLayer(OffscreenBuffer*) { ADD_FAILURE(); }
virtual void endLayer() { ADD_FAILURE(); }
virtual void startFrame(uint32_t width, uint32_t height) {}
virtual void endFrame() {}
@@ -192,7 +195,8 @@ TEST(OpReorderer, renderNode) {
std::vector< sp<RenderNode> > nodes;
nodes.push_back(parent.get());
OpReorderer reorderer(SkRect::MakeWH(200, 200), 200, 200, nodes);
OpReorderer reorderer(sEmptyLayerUpdateQueue,
SkRect::MakeWH(200, 200), 200, 200, nodes);
RenderNodeTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
@@ -216,7 +220,8 @@ TEST(OpReorderer, clipped) {
std::vector< sp<RenderNode> > nodes;
nodes.push_back(node.get());
OpReorderer reorderer(SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
OpReorderer reorderer(sEmptyLayerUpdateQueue,
SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
200, 200, nodes);
ClippedTestRenderer renderer;
@@ -226,7 +231,7 @@ TEST(OpReorderer, clipped) {
class SaveLayerSimpleTestRenderer : public TestRendererBase {
public:
OffscreenBuffer* startLayer(uint32_t width, uint32_t height) override {
OffscreenBuffer* createLayer(uint32_t width, uint32_t height) override {
EXPECT_EQ(0, mIndex++);
EXPECT_EQ(180u, width);
EXPECT_EQ(180u, height);
@@ -268,13 +273,13 @@ TEST(OpReorderer, saveLayerSimple) {
/* saveLayer1 {rect1, saveLayer2 { rect2 } } will play back as:
* - startLayer2, rect2 endLayer2
* - startLayer1, rect1, drawLayer2, endLayer1
* - createLayer2, rect2 endLayer2
* - createLayer1, rect1, drawLayer2, endLayer1
* - startFrame, layerOp1, endFrame
*/
class SaveLayerNestedTestRenderer : public TestRendererBase {
public:
OffscreenBuffer* startLayer(uint32_t width, uint32_t height) override {
OffscreenBuffer* createLayer(uint32_t width, uint32_t height) override {
const int index = mIndex++;
if (index == 0) {
EXPECT_EQ(400u, width);
@@ -356,5 +361,162 @@ TEST(OpReorderer, saveLayerContentRejection) {
reorderer.replayBakedOps<TestDispatcher>(renderer);
}
class HwLayerSimpleTestRenderer : public TestRendererBase {
public:
void startLayer(OffscreenBuffer* offscreenBuffer) override {
EXPECT_EQ(0, mIndex++);
EXPECT_EQ(offscreenBuffer, (OffscreenBuffer*) 0x0124);
}
void onRectOp(const RectOp& op, const BakedOpState& state) override {
EXPECT_EQ(1, mIndex++);
// verify transform is reset
EXPECT_TRUE(state.computedState.transform.isIdentity());
// verify damage rect is used as clip
EXPECT_EQ(state.computedState.clipRect, Rect(25, 25, 75, 75));
}
void endLayer() override {
EXPECT_EQ(2, mIndex++);
}
void startFrame(uint32_t width, uint32_t height) override {
EXPECT_EQ(3, mIndex++);
}
void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
EXPECT_EQ(4, mIndex++);
}
void endFrame() override {
EXPECT_EQ(5, mIndex++);
}
};
TEST(OpReorderer, hwLayerSimple) {
sp<RenderNode> node = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110, [](RecordingCanvas& canvas) {
SkPaint paint;
paint.setColor(SK_ColorWHITE);
canvas.drawRect(0, 0, 100, 100, paint);
});
node->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
node->setPropertyFieldsDirty(RenderNode::GENERIC);
OffscreenBuffer** bufferHandle = node->getLayerHandle();
*bufferHandle = (OffscreenBuffer*) 0x0124;
TestUtils::syncNodePropertiesAndDisplayList(node);
std::vector< sp<RenderNode> > nodes;
nodes.push_back(node.get());
// only enqueue partial damage
LayerUpdateQueue layerUpdateQueue;
layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
OpReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, nodes);
HwLayerSimpleTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(6, renderer.getIndex());
// clean up layer pointer, so we can safely destruct RenderNode
*bufferHandle = nullptr;
}
/* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:
* - startLayer(child), rect(grey), endLayer
* - createLayer, drawLayer(child), endLayer
* - startLayer(parent), rect(white), drawLayer(saveLayer), endLayer
* - startFrame, drawLayer(parent), endLayerb
*/
class HwLayerComplexTestRenderer : public TestRendererBase {
public:
OffscreenBuffer* createLayer(uint32_t width, uint32_t height) {
EXPECT_EQ(3, mIndex++); // savelayer first
return (OffscreenBuffer*)0xabcd;
}
void startLayer(OffscreenBuffer* offscreenBuffer) override {
int index = mIndex++;
if (index == 0) {
// starting inner layer
EXPECT_EQ((OffscreenBuffer*)0x4567, offscreenBuffer);
} else if (index == 6) {
// starting outer layer
EXPECT_EQ((OffscreenBuffer*)0x0123, offscreenBuffer);
} else { ADD_FAILURE(); }
}
void onRectOp(const RectOp& op, const BakedOpState& state) override {
int index = mIndex++;
if (index == 1) {
// inner layer's rect (white)
EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
} else if (index == 7) {
// outer layer's rect (grey)
EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
} else { ADD_FAILURE(); }
}
void endLayer() override {
int index = mIndex++;
EXPECT_TRUE(index == 2 || index == 5 || index == 9);
}
void startFrame(uint32_t width, uint32_t height) override {
EXPECT_EQ(10, mIndex++);
}
void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
int index = mIndex++;
if (index == 4) {
EXPECT_EQ((OffscreenBuffer*)0x4567, *op.layerHandle);
} else if (index == 8) {
EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle);
} else if (index == 11) {
EXPECT_EQ((OffscreenBuffer*)0x0123, *op.layerHandle);
} else { ADD_FAILURE(); }
}
void endFrame() override {
EXPECT_EQ(12, mIndex++);
}
};
TEST(OpReorderer, hwLayerComplex) {
sp<RenderNode> child = TestUtils::createNode<RecordingCanvas>(50, 50, 150, 150, [](RecordingCanvas& canvas) {
SkPaint paint;
paint.setColor(SK_ColorWHITE);
canvas.drawRect(0, 0, 100, 100, paint);
});
child->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
child->setPropertyFieldsDirty(RenderNode::GENERIC);
*(child->getLayerHandle()) = (OffscreenBuffer*) 0x4567;
RenderNode* childPtr = child.get();
sp<RenderNode> parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [childPtr](RecordingCanvas& canvas) {
SkPaint paint;
paint.setColor(SK_ColorDKGRAY);
canvas.drawRect(0, 0, 200, 200, paint);
canvas.saveLayerAlpha(50, 50, 150, 150, 128, SkCanvas::kClipToLayer_SaveFlag);
canvas.drawRenderNode(childPtr);
canvas.restore();
});
parent->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
parent->setPropertyFieldsDirty(RenderNode::GENERIC);
*(parent->getLayerHandle()) = (OffscreenBuffer*) 0x0123;
TestUtils::syncNodePropertiesAndDisplayList(child);
TestUtils::syncNodePropertiesAndDisplayList(parent);
std::vector< sp<RenderNode> > nodes;
nodes.push_back(parent.get());
LayerUpdateQueue layerUpdateQueue;
layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100));
layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200));
OpReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, nodes);
HwLayerComplexTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(13, renderer.getIndex());
// clean up layer pointers, so we can safely destruct RenderNodes
*(child->getLayerHandle()) = nullptr;
*(parent->getLayerHandle()) = nullptr;
}
} // namespace uirenderer
} // namespace android

View File

@@ -89,15 +89,24 @@ public:
return std::unique_ptr<DisplayList>(canvas.finishRecording());
}
template<class CanvasType>
static sp<RenderNode> createNode(int left, int top, int right, int bottom,
std::function<void(CanvasType& canvas)> canvasCallback) {
static sp<RenderNode> createNode(int left, int top, int right, int bottom, bool onLayer = false) {
sp<RenderNode> node = new RenderNode();
node->mutateStagingProperties().setLeftTopRightBottom(left, top, right, bottom);
node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
if (onLayer) {
node->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
node->setPropertyFieldsDirty(RenderNode::GENERIC);
}
return node;
}
CanvasType canvas(
node->stagingProperties().getWidth(), node->stagingProperties().getHeight());
template<class CanvasType>
static sp<RenderNode> createNode(int left, int top, int right, int bottom,
std::function<void(CanvasType& canvas)> canvasCallback, bool onLayer = false) {
sp<RenderNode> node = createNode(left, top, right, bottom, onLayer);
auto&& props = node->stagingProperties(); // staging, since not sync'd yet
CanvasType canvas(props.getWidth(), props.getHeight());
canvasCallback(canvas);
node->setStagingDisplayList(canvas.finishRecording());
return node;
@@ -108,7 +117,7 @@ public:
node->syncDisplayList();
}
typedef std::function<void(RenderState& state, Caches& caches)> RtCallback;
typedef std::function<void(renderthread::RenderThread& thread)> RtCallback;
class TestTask : public renderthread::RenderTask {
public:
@@ -120,7 +129,7 @@ public:
RenderState& renderState = renderthread::RenderThread::getInstance().renderState();
renderState.onGLContextCreated();
rtCallback(renderState, Caches::getInstance());
rtCallback(renderthread::RenderThread::getInstance());
renderState.onGLContextDestroyed();
};
RtCallback rtCallback;