Merge "Support projection in OpReorderer"
This commit is contained in:
@@ -276,7 +276,9 @@ LOCAL_MULTILIB := both
|
||||
LOCAL_MODULE_STEM_32 := hwuimicro
|
||||
LOCAL_MODULE_STEM_64 := hwuimicro64
|
||||
LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
|
||||
LOCAL_CFLAGS := $(hwui_cflags)
|
||||
LOCAL_CFLAGS := \
|
||||
$(hwui_cflags) \
|
||||
-DHWUI_NULL_GPU
|
||||
LOCAL_C_INCLUDES += bionic/benchmarks/
|
||||
|
||||
LOCAL_WHOLE_STATIC_LIBRARIES := libhwui_static_null_gpu
|
||||
|
||||
@@ -132,7 +132,7 @@ public:
|
||||
DisplayList();
|
||||
~DisplayList();
|
||||
|
||||
// index of DisplayListOp restore, after which projected descendents should be drawn
|
||||
// index of DisplayListOp restore, after which projected descendants should be drawn
|
||||
int projectionReceiveIndex;
|
||||
|
||||
const LsaVector<Chunk>& getChunks() const { return chunks; }
|
||||
|
||||
@@ -55,6 +55,7 @@ class DeferredDisplayList;
|
||||
class DeferredLayerUpdater;
|
||||
class DisplayListOp;
|
||||
class DrawOp;
|
||||
class DrawRenderNodeOp;
|
||||
class RenderNode;
|
||||
class StateOp;
|
||||
|
||||
|
||||
@@ -1386,19 +1386,19 @@ public:
|
||||
: DrawBoundedOp(0, 0, renderNode->getWidth(), renderNode->getHeight(), nullptr)
|
||||
, renderNode(renderNode)
|
||||
, mRecordedWithPotentialStencilClip(!clipIsSimple || !transformFromParent.isSimple())
|
||||
, mTransformFromParent(transformFromParent)
|
||||
, mSkipInOrderDraw(false) {}
|
||||
, localMatrix(transformFromParent)
|
||||
, skipInOrderDraw(false) {}
|
||||
|
||||
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
|
||||
bool useQuickReject) override {
|
||||
if (renderNode->isRenderable() && !mSkipInOrderDraw) {
|
||||
if (renderNode->isRenderable() && !skipInOrderDraw) {
|
||||
renderNode->defer(deferStruct, level + 1);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level,
|
||||
bool useQuickReject) override {
|
||||
if (renderNode->isRenderable() && !mSkipInOrderDraw) {
|
||||
if (renderNode->isRenderable() && !skipInOrderDraw) {
|
||||
renderNode->replay(replayStruct, level + 1);
|
||||
}
|
||||
}
|
||||
@@ -1439,7 +1439,7 @@ private:
|
||||
/**
|
||||
* Records transform vs parent, used for computing total transform without rerunning DL contents
|
||||
*/
|
||||
const mat4 mTransformFromParent;
|
||||
const mat4 localMatrix;
|
||||
|
||||
/**
|
||||
* Holds the transformation between the projection surface ViewGroup and this RenderNode
|
||||
@@ -1449,8 +1449,8 @@ private:
|
||||
*
|
||||
* Note: doesn't include transformation within the RenderNode, or its properties.
|
||||
*/
|
||||
mat4 mTransformFromCompositingAncestor;
|
||||
bool mSkipInOrderDraw;
|
||||
mat4 transformFromCompositingAncestor;
|
||||
bool skipInOrderDraw;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -330,6 +330,7 @@ OpReorderer::OpReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
|
||||
for (int i = layers.entries().size() - 1; i >= 0; i--) {
|
||||
RenderNode* layerNode = layers.entries()[i].renderNode;
|
||||
const Rect& layerDamage = layers.entries()[i].damage;
|
||||
layerNode->computeOrdering();
|
||||
|
||||
// map current light center into RenderNode's coordinate space
|
||||
Vector3 lightCenter = mCanvasState.currentSnapshot()->getRelativeLightCenter();
|
||||
@@ -339,7 +340,7 @@ OpReorderer::OpReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
|
||||
layerDamage, lightCenter, nullptr, layerNode);
|
||||
|
||||
if (layerNode->getDisplayList()) {
|
||||
deferDisplayList(*(layerNode->getDisplayList()));
|
||||
deferNodeOps(*layerNode);
|
||||
}
|
||||
restoreForLayer();
|
||||
}
|
||||
@@ -347,6 +348,7 @@ OpReorderer::OpReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
|
||||
// Defer Fbo0
|
||||
for (const sp<RenderNode>& node : nodes) {
|
||||
if (node->nothingToDraw()) continue;
|
||||
node->computeOrdering();
|
||||
|
||||
int count = mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
|
||||
deferNodePropsAndOps(*node);
|
||||
@@ -354,20 +356,6 @@ OpReorderer::OpReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
|
||||
}
|
||||
}
|
||||
|
||||
OpReorderer::OpReorderer(int viewportWidth, int viewportHeight, const DisplayList& displayList,
|
||||
const Vector3& lightCenter)
|
||||
: mCanvasState(*this) {
|
||||
ATRACE_NAME("prepare drawing commands");
|
||||
// Prepare to defer Fbo0
|
||||
mLayerReorderers.emplace_back(viewportWidth, viewportHeight,
|
||||
Rect(viewportWidth, viewportHeight));
|
||||
mLayerStack.push_back(0);
|
||||
mCanvasState.initializeSaveStack(viewportWidth, viewportHeight,
|
||||
0, 0, viewportWidth, viewportHeight, lightCenter);
|
||||
|
||||
deferDisplayList(displayList);
|
||||
}
|
||||
|
||||
void OpReorderer::onViewportInitialized() {}
|
||||
|
||||
void OpReorderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
|
||||
@@ -462,10 +450,10 @@ void OpReorderer::deferNodePropsAndOps(RenderNode& node) {
|
||||
Matrix4::identity(),
|
||||
saveLayerBounds,
|
||||
&saveLayerPaint));
|
||||
deferDisplayList(*(node.getDisplayList()));
|
||||
deferNodeOps(node);
|
||||
onEndLayerOp(*new (mAllocator) EndLayerOp());
|
||||
} else {
|
||||
deferDisplayList(*(node.getDisplayList()));
|
||||
deferNodeOps(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -610,18 +598,53 @@ void OpReorderer::deferShadow(const RenderNodeOp& casterNodeOp) {
|
||||
}
|
||||
}
|
||||
|
||||
void OpReorderer::deferProjectedChildren(const RenderNode& renderNode) {
|
||||
const SkPath* projectionReceiverOutline = renderNode.properties().getOutline().getPath();
|
||||
int count = mCanvasState.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
|
||||
|
||||
// can't be null, since DL=null node rejection happens before deferNodePropsAndOps
|
||||
const DisplayList& displayList = *(renderNode.getDisplayList());
|
||||
|
||||
const RecordedOp* op = (displayList.getOps()[displayList.projectionReceiveIndex]);
|
||||
const RenderNodeOp* backgroundOp = static_cast<const RenderNodeOp*>(op);
|
||||
const RenderProperties& backgroundProps = backgroundOp->renderNode->properties();
|
||||
|
||||
// Transform renderer to match background we're projecting onto
|
||||
// (by offsetting canvas by translationX/Y of background rendernode, since only those are set)
|
||||
mCanvasState.translate(backgroundProps.getTranslationX(), backgroundProps.getTranslationY());
|
||||
|
||||
// If the projection receiver has an outline, we mask projected content to it
|
||||
// (which we know, apriori, are all tessellated paths)
|
||||
mCanvasState.setProjectionPathMask(mAllocator, projectionReceiverOutline);
|
||||
|
||||
// draw projected nodes
|
||||
for (size_t i = 0; i < renderNode.mProjectedNodes.size(); i++) {
|
||||
RenderNodeOp* childOp = renderNode.mProjectedNodes[i];
|
||||
|
||||
int restoreTo = mCanvasState.save(SkCanvas::kMatrix_SaveFlag);
|
||||
mCanvasState.concatMatrix(childOp->transformFromCompositingAncestor);
|
||||
deferRenderNodeOp(*childOp);
|
||||
mCanvasState.restoreToCount(restoreTo);
|
||||
}
|
||||
|
||||
mCanvasState.restoreToCount(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to define a list of lambdas referencing private OpReorderer::onXXXXOp() methods.
|
||||
*
|
||||
* This allows opIds embedded in the RecordedOps to be used for dispatching to these lambdas. E.g. a
|
||||
* BitmapOp op then would be dispatched to OpReorderer::onBitmapOp(const BitmapOp&)
|
||||
* This allows opIds embedded in the RecordedOps to be used for dispatching to these lambdas.
|
||||
* E.g. a BitmapOp op then would be dispatched to OpReorderer::onBitmapOp(const BitmapOp&)
|
||||
*/
|
||||
#define OP_RECEIVER(Type) \
|
||||
[](OpReorderer& reorderer, const RecordedOp& op) { reorderer.on##Type(static_cast<const Type&>(op)); },
|
||||
void OpReorderer::deferDisplayList(const DisplayList& displayList) {
|
||||
void OpReorderer::deferNodeOps(const RenderNode& renderNode) {
|
||||
static std::function<void(OpReorderer& reorderer, const RecordedOp&)> receivers[] = {
|
||||
MAP_OPS(OP_RECEIVER)
|
||||
};
|
||||
|
||||
// can't be null, since DL=null node rejection happens before deferNodePropsAndOps
|
||||
const DisplayList& displayList = *(renderNode.getDisplayList());
|
||||
for (const DisplayList::Chunk& chunk : displayList.getChunks()) {
|
||||
FatVector<ZRenderNodeOpPair, 16> zTranslatedNodes;
|
||||
buildZSortedChildList(&zTranslatedNodes, displayList, chunk);
|
||||
@@ -630,6 +653,12 @@ void OpReorderer::deferDisplayList(const DisplayList& displayList) {
|
||||
for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
|
||||
const RecordedOp* op = displayList.getOps()[opIndex];
|
||||
receivers[op->opId](*this, *op);
|
||||
|
||||
if (CC_UNLIKELY(!renderNode.mProjectedNodes.empty()
|
||||
&& displayList.projectionReceiveIndex >= 0
|
||||
&& static_cast<int>(opIndex) == displayList.projectionReceiveIndex)) {
|
||||
deferProjectedChildren(renderNode);
|
||||
}
|
||||
}
|
||||
defer3dChildren(ChildrenSelectMode::Positive, zTranslatedNodes);
|
||||
}
|
||||
|
||||
@@ -124,9 +124,6 @@ public:
|
||||
uint32_t viewportWidth, uint32_t viewportHeight,
|
||||
const std::vector< sp<RenderNode> >& nodes, const Vector3& lightCenter);
|
||||
|
||||
OpReorderer(int viewportWidth, int viewportHeight, const DisplayList& displayList,
|
||||
const Vector3& lightCenter);
|
||||
|
||||
virtual ~OpReorderer() {}
|
||||
|
||||
/**
|
||||
@@ -202,16 +199,18 @@ private:
|
||||
return BakedOpState::tryConstruct(mAllocator, *mCanvasState.currentSnapshot(), recordedOp);
|
||||
}
|
||||
|
||||
// should always be surrounded by a save/restore pair
|
||||
// should always be surrounded by a save/restore pair, and not called if DisplayList is null
|
||||
void deferNodePropsAndOps(RenderNode& node);
|
||||
|
||||
void deferShadow(const RenderNodeOp& casterOp);
|
||||
|
||||
void deferDisplayList(const DisplayList& displayList);
|
||||
|
||||
template <typename V>
|
||||
void defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedNodes);
|
||||
|
||||
void deferShadow(const RenderNodeOp& casterOp);
|
||||
|
||||
void deferProjectedChildren(const RenderNode& renderNode);
|
||||
|
||||
void deferNodeOps(const RenderNode& renderNode);
|
||||
|
||||
void deferRenderNodeOp(const RenderNodeOp& op);
|
||||
|
||||
void replayBakedOpsImpl(void* arg, BakedOpDispatcher* receivers);
|
||||
|
||||
@@ -97,7 +97,17 @@ struct RenderNodeOp : RecordedOp {
|
||||
RenderNodeOp(BASE_PARAMS_PAINTLESS, RenderNode* renderNode)
|
||||
: SUPER_PAINTLESS(RenderNodeOp)
|
||||
, renderNode(renderNode) {}
|
||||
RenderNode * renderNode; // not const, since drawing modifies it (somehow...)
|
||||
RenderNode * renderNode; // not const, since drawing modifies it
|
||||
|
||||
/**
|
||||
* Holds the transformation between the projection surface ViewGroup and this RenderNode
|
||||
* drawing instance. Represents any translations / transformations done within the drawing of
|
||||
* the compositing ancestor ViewGroup's draw, before the draw of the View represented by this
|
||||
* DisplayList draw instance.
|
||||
*
|
||||
* Note: doesn't include transformation within the RenderNode, or its properties.
|
||||
*/
|
||||
Matrix4 transformFromCompositingAncestor;
|
||||
bool skipInOrderDraw = false;
|
||||
};
|
||||
|
||||
|
||||
@@ -487,7 +487,7 @@ void RenderNode::prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayL
|
||||
info.damageAccumulator->pushTransform(&op->localMatrix);
|
||||
bool childFunctorsNeedLayer = functorsNeedLayer; // TODO! || op->mRecordedWithPotentialStencilClip;
|
||||
#else
|
||||
info.damageAccumulator->pushTransform(&op->mTransformFromParent);
|
||||
info.damageAccumulator->pushTransform(&op->localMatrix);
|
||||
bool childFunctorsNeedLayer = functorsNeedLayer
|
||||
// Recorded with non-rect clip, or canvas-rotated by parent
|
||||
|| op->mRecordedWithPotentialStencilClip;
|
||||
@@ -658,7 +658,6 @@ void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform)
|
||||
* which are flagged to not draw in the standard draw loop.
|
||||
*/
|
||||
void RenderNode::computeOrdering() {
|
||||
#if !HWUI_NEW_OPS
|
||||
ATRACE_CALL();
|
||||
mProjectedNodes.clear();
|
||||
|
||||
@@ -666,43 +665,41 @@ void RenderNode::computeOrdering() {
|
||||
// transform properties are applied correctly to top level children
|
||||
if (mDisplayList == nullptr) return;
|
||||
for (unsigned int i = 0; i < mDisplayList->getChildren().size(); i++) {
|
||||
DrawRenderNodeOp* childOp = mDisplayList->getChildren()[i];
|
||||
renderNodeOp_t* childOp = mDisplayList->getChildren()[i];
|
||||
childOp->renderNode->computeOrderingImpl(childOp, &mProjectedNodes, &mat4::identity());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void RenderNode::computeOrderingImpl(
|
||||
DrawRenderNodeOp* opState,
|
||||
std::vector<DrawRenderNodeOp*>* compositedChildrenOfProjectionSurface,
|
||||
renderNodeOp_t* opState,
|
||||
std::vector<renderNodeOp_t*>* compositedChildrenOfProjectionSurface,
|
||||
const mat4* transformFromProjectionSurface) {
|
||||
#if !HWUI_NEW_OPS
|
||||
mProjectedNodes.clear();
|
||||
if (mDisplayList == nullptr || mDisplayList->isEmpty()) return;
|
||||
|
||||
// TODO: should avoid this calculation in most cases
|
||||
// TODO: just calculate single matrix, down to all leaf composited elements
|
||||
Matrix4 localTransformFromProjectionSurface(*transformFromProjectionSurface);
|
||||
localTransformFromProjectionSurface.multiply(opState->mTransformFromParent);
|
||||
localTransformFromProjectionSurface.multiply(opState->localMatrix);
|
||||
|
||||
if (properties().getProjectBackwards()) {
|
||||
// composited projectee, flag for out of order draw, save matrix, and store in proj surface
|
||||
opState->mSkipInOrderDraw = true;
|
||||
opState->mTransformFromCompositingAncestor = localTransformFromProjectionSurface;
|
||||
opState->skipInOrderDraw = true;
|
||||
opState->transformFromCompositingAncestor = localTransformFromProjectionSurface;
|
||||
compositedChildrenOfProjectionSurface->push_back(opState);
|
||||
} else {
|
||||
// standard in order draw
|
||||
opState->mSkipInOrderDraw = false;
|
||||
opState->skipInOrderDraw = false;
|
||||
}
|
||||
|
||||
if (mDisplayList->getChildren().size() > 0) {
|
||||
const bool isProjectionReceiver = mDisplayList->projectionReceiveIndex >= 0;
|
||||
bool haveAppliedPropertiesToProjection = false;
|
||||
for (unsigned int i = 0; i < mDisplayList->getChildren().size(); i++) {
|
||||
DrawRenderNodeOp* childOp = mDisplayList->getChildren()[i];
|
||||
renderNodeOp_t* childOp = mDisplayList->getChildren()[i];
|
||||
RenderNode* child = childOp->renderNode;
|
||||
|
||||
std::vector<DrawRenderNodeOp*>* projectionChildren = nullptr;
|
||||
std::vector<renderNodeOp_t*>* projectionChildren = nullptr;
|
||||
const mat4* projectionTransform = nullptr;
|
||||
if (isProjectionReceiver && !child->properties().getProjectBackwards()) {
|
||||
// if receiving projections, collect projecting descendant
|
||||
@@ -723,7 +720,6 @@ void RenderNode::computeOrderingImpl(
|
||||
child->computeOrderingImpl(childOp, projectionChildren, projectionTransform);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
class DeferOperationHandler {
|
||||
@@ -793,10 +789,10 @@ void RenderNode::buildZSortedChildList(const DisplayList::Chunk& chunk,
|
||||
|
||||
if (!MathUtils::isZero(childZ) && chunk.reorderChildren) {
|
||||
zTranslatedNodes.push_back(ZDrawRenderNodeOpPair(childZ, childOp));
|
||||
childOp->mSkipInOrderDraw = true;
|
||||
childOp->skipInOrderDraw = true;
|
||||
} else if (!child->properties().getProjectBackwards()) {
|
||||
// regular, in order drawing DisplayList
|
||||
childOp->mSkipInOrderDraw = false;
|
||||
childOp->skipInOrderDraw = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -913,7 +909,7 @@ void RenderNode::issueOperationsOf3dChildren(ChildrenSelectMode mode,
|
||||
// attempt to render the shadow if the caster about to be drawn is its caster,
|
||||
// OR if its caster's Z value is similar to the previous potential caster
|
||||
if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) {
|
||||
caster->issueDrawShadowOperation(casterOp->mTransformFromParent, handler);
|
||||
caster->issueDrawShadowOperation(casterOp->localMatrix, handler);
|
||||
|
||||
lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
|
||||
shadowIndex++;
|
||||
@@ -927,10 +923,10 @@ void RenderNode::issueOperationsOf3dChildren(ChildrenSelectMode mode,
|
||||
|
||||
DrawRenderNodeOp* childOp = zTranslatedNodes[drawIndex].value;
|
||||
|
||||
renderer.concatMatrix(childOp->mTransformFromParent);
|
||||
childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
|
||||
renderer.concatMatrix(childOp->localMatrix);
|
||||
childOp->skipInOrderDraw = false; // this is horrible, I'm so sorry everyone
|
||||
handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds());
|
||||
childOp->mSkipInOrderDraw = true;
|
||||
childOp->skipInOrderDraw = true;
|
||||
|
||||
renderer.restoreToCount(restoreTo);
|
||||
drawIndex++;
|
||||
@@ -967,14 +963,14 @@ void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T&
|
||||
|
||||
// draw projected nodes
|
||||
for (size_t i = 0; i < mProjectedNodes.size(); i++) {
|
||||
DrawRenderNodeOp* childOp = mProjectedNodes[i];
|
||||
renderNodeOp_t* childOp = mProjectedNodes[i];
|
||||
|
||||
// matrix save, concat, and restore can be done safely without allocating operations
|
||||
int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
|
||||
renderer.concatMatrix(childOp->mTransformFromCompositingAncestor);
|
||||
childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
|
||||
renderer.concatMatrix(childOp->transformFromCompositingAncestor);
|
||||
childOp->skipInOrderDraw = false; // this is horrible, I'm so sorry everyone
|
||||
handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds());
|
||||
childOp->mSkipInOrderDraw = true;
|
||||
childOp->skipInOrderDraw = true;
|
||||
renderer.restoreToCount(restoreTo);
|
||||
}
|
||||
|
||||
|
||||
@@ -51,20 +51,22 @@ class OpReorderer;
|
||||
class Rect;
|
||||
class SkiaShader;
|
||||
|
||||
|
||||
#if HWUI_NEW_OPS
|
||||
class OffscreenBuffer;
|
||||
struct RenderNodeOp;
|
||||
typedef OffscreenBuffer layer_t;
|
||||
typedef RenderNodeOp renderNodeOp_t;
|
||||
#else
|
||||
class Layer;
|
||||
typedef Layer layer_t;
|
||||
typedef DrawRenderNodeOp renderNodeOp_t;
|
||||
#endif
|
||||
|
||||
class ClipRectOp;
|
||||
class DrawRenderNodeOp;
|
||||
class SaveLayerOp;
|
||||
class SaveOp;
|
||||
class RestoreToCountOp;
|
||||
class DrawRenderNodeOp;
|
||||
class TreeInfo;
|
||||
|
||||
namespace proto {
|
||||
@@ -85,6 +87,7 @@ class RenderNode;
|
||||
*/
|
||||
class RenderNode : public VirtualLightRefBase {
|
||||
friend class TestUtils; // allow TestUtils to access syncDisplayList / syncProperties
|
||||
friend class OpReorderer;
|
||||
public:
|
||||
enum DirtyPropertyMask {
|
||||
GENERIC = 1 << 1,
|
||||
@@ -221,8 +224,8 @@ private:
|
||||
PositiveZChildren
|
||||
};
|
||||
|
||||
void computeOrderingImpl(DrawRenderNodeOp* opState,
|
||||
std::vector<DrawRenderNodeOp*>* compositedChildrenOfProjectionSurface,
|
||||
void computeOrderingImpl(renderNodeOp_t* opState,
|
||||
std::vector<renderNodeOp_t*>* compositedChildrenOfProjectionSurface,
|
||||
const mat4* transformFromProjectionSurface);
|
||||
|
||||
template <class T>
|
||||
@@ -305,7 +308,7 @@ private:
|
||||
*/
|
||||
|
||||
// for projection surfaces, contains a list of all children items
|
||||
std::vector<DrawRenderNodeOp*> mProjectedNodes;
|
||||
std::vector<renderNodeOp_t*> mProjectedNodes;
|
||||
|
||||
// How many references our parent(s) have to us. Typically this should alternate
|
||||
// between 2 and 1 (when a staging push happens we inc first then dec)
|
||||
|
||||
@@ -19,10 +19,12 @@
|
||||
#include "BakedOpState.h"
|
||||
#include "BakedOpDispatcher.h"
|
||||
#include "BakedOpRenderer.h"
|
||||
#include "LayerUpdateQueue.h"
|
||||
#include "OpReorderer.h"
|
||||
#include "RecordedOp.h"
|
||||
#include "RecordingCanvas.h"
|
||||
#include "utils/TestUtils.h"
|
||||
#include "Vector.h"
|
||||
#include "microbench/MicroBench.h"
|
||||
|
||||
#include <vector>
|
||||
@@ -30,26 +32,38 @@
|
||||
using namespace android;
|
||||
using namespace android::uirenderer;
|
||||
|
||||
auto sReorderingDisplayList = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
|
||||
SkBitmap bitmap = TestUtils::createSkBitmap(10, 10);
|
||||
SkPaint paint;
|
||||
const LayerUpdateQueue sEmptyLayerUpdateQueue;
|
||||
const Vector3 sLightCenter = {100, 100, 100};
|
||||
|
||||
// Alternate between drawing rects and bitmaps, with bitmaps overlapping rects.
|
||||
// Rects don't overlap bitmaps, so bitmaps should be brought to front as a group.
|
||||
canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
|
||||
for (int i = 0; i < 30; i++) {
|
||||
canvas.translate(0, 10);
|
||||
canvas.drawRect(0, 0, 10, 10, paint);
|
||||
canvas.drawBitmap(bitmap, 5, 0, nullptr);
|
||||
}
|
||||
canvas.restore();
|
||||
});
|
||||
static std::vector<sp<RenderNode>> createTestNodeList() {
|
||||
auto node = TestUtils::createNode(0, 0, 200, 200,
|
||||
[](RenderProperties& props, RecordingCanvas& canvas) {
|
||||
SkBitmap bitmap = TestUtils::createSkBitmap(10, 10);
|
||||
SkPaint paint;
|
||||
|
||||
// Alternate between drawing rects and bitmaps, with bitmaps overlapping rects.
|
||||
// Rects don't overlap bitmaps, so bitmaps should be brought to front as a group.
|
||||
canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
|
||||
for (int i = 0; i < 30; i++) {
|
||||
canvas.translate(0, 10);
|
||||
canvas.drawRect(0, 0, 10, 10, paint);
|
||||
canvas.drawBitmap(bitmap, 5, 0, nullptr);
|
||||
}
|
||||
canvas.restore();
|
||||
});
|
||||
TestUtils::syncHierarchyPropertiesAndDisplayList(node);
|
||||
std::vector<sp<RenderNode>> vec;
|
||||
vec.emplace_back(node);
|
||||
return vec;
|
||||
}
|
||||
|
||||
BENCHMARK_NO_ARG(BM_OpReorderer_defer);
|
||||
void BM_OpReorderer_defer::Run(int iters) {
|
||||
auto nodes = createTestNodeList();
|
||||
StartBenchmarkTiming();
|
||||
for (int i = 0; i < iters; i++) {
|
||||
OpReorderer reorderer(200, 200, *sReorderingDisplayList, (Vector3) { 100, 100, 100 });
|
||||
OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
|
||||
nodes, sLightCenter);
|
||||
MicroBench::DoNotOptimize(&reorderer);
|
||||
}
|
||||
StopBenchmarkTiming();
|
||||
@@ -58,13 +72,16 @@ void BM_OpReorderer_defer::Run(int iters) {
|
||||
BENCHMARK_NO_ARG(BM_OpReorderer_deferAndRender);
|
||||
void BM_OpReorderer_deferAndRender::Run(int iters) {
|
||||
TestUtils::runOnRenderThread([this, iters](renderthread::RenderThread& thread) {
|
||||
auto nodes = createTestNodeList();
|
||||
BakedOpRenderer::LightInfo lightInfo = {50.0f, 128, 128 };
|
||||
|
||||
RenderState& renderState = thread.renderState();
|
||||
Caches& caches = Caches::getInstance();
|
||||
BakedOpRenderer::LightInfo lightInfo = { 50.0f, 128, 128 };
|
||||
|
||||
StartBenchmarkTiming();
|
||||
for (int i = 0; i < iters; i++) {
|
||||
OpReorderer reorderer(200, 200, *sReorderingDisplayList, (Vector3) { 100, 100, 100 });
|
||||
OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
|
||||
nodes, sLightCenter);
|
||||
|
||||
BakedOpRenderer renderer(caches, renderState, true, lightInfo);
|
||||
reorderer.replayBakedOps<BakedOpDispatcher>(renderer);
|
||||
|
||||
@@ -29,17 +29,14 @@ public:
|
||||
sp<RenderNode> card;
|
||||
void createContent(int width, int height, TestCanvas& canvas) override {
|
||||
canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
|
||||
canvas.insertReorderBarrier(true);
|
||||
|
||||
card = TestUtils::createNode(0, 0, 200, 200, [](TestCanvas& canvas) {
|
||||
card = TestUtils::createNode(0, 0, 200, 200,
|
||||
[](RenderProperties& props, TestCanvas& canvas) {
|
||||
SkPaint paint;
|
||||
paint.setAntiAlias(true);
|
||||
paint.setColor(0xFF000000);
|
||||
canvas.drawOval(0, 0, 200, 200, paint);
|
||||
});
|
||||
|
||||
canvas.drawRenderNode(card.get());
|
||||
canvas.insertReorderBarrier(false);
|
||||
}
|
||||
|
||||
void doFrame(int frameNr) override {
|
||||
|
||||
@@ -44,7 +44,7 @@ public:
|
||||
SkColor color = COLORS[static_cast<int>((y / dp(116))) % 4];
|
||||
sp<RenderNode> card = TestUtils::createNode(x, y,
|
||||
x + dp(100), y + dp(100),
|
||||
[color](TestCanvas& canvas) {
|
||||
[color](RenderProperties& props, TestCanvas& canvas) {
|
||||
canvas.drawColor(color, SkXfermode::kSrcOver_Mode);
|
||||
});
|
||||
canvas.drawRenderNode(card.get());
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
canvas.insertReorderBarrier(true);
|
||||
|
||||
card = TestUtils::createNode(50, 50, 250, 250,
|
||||
[](TestCanvas& canvas) {
|
||||
[](RenderProperties& props, TestCanvas& canvas) {
|
||||
canvas.drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode);
|
||||
|
||||
SkRegion region;
|
||||
|
||||
@@ -32,7 +32,7 @@ public:
|
||||
canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); // background
|
||||
|
||||
card = TestUtils::createNode(0, 0, 200, 200,
|
||||
[](TestCanvas& canvas) {
|
||||
[](RenderProperties& props, TestCanvas& canvas) {
|
||||
canvas.saveLayerAlpha(0, 0, 200, 200, 128, SkCanvas::kClipToLayer_SaveFlag);
|
||||
canvas.drawColor(0xFF00FF00, SkXfermode::kSrcOver_Mode); // outer, unclipped
|
||||
canvas.saveLayerAlpha(50, 50, 150, 150, 128, SkCanvas::kClipToLayer_SaveFlag);
|
||||
|
||||
@@ -31,7 +31,7 @@ TEST(LayerUpdateQueue, construct) {
|
||||
|
||||
// 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);
|
||||
sp<RenderNode> node = TestUtils::createNode(0, 0, width, height, nullptr);
|
||||
TestUtils::syncHierarchyPropertiesAndDisplayList(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -111,25 +111,29 @@ TEST(OpReorderer, simple) {
|
||||
}
|
||||
};
|
||||
|
||||
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
|
||||
auto node = TestUtils::createNode(0, 0, 100, 200,
|
||||
[](RenderProperties& props, RecordingCanvas& canvas) {
|
||||
SkBitmap bitmap = TestUtils::createSkBitmap(25, 25);
|
||||
canvas.drawRect(0, 0, 100, 200, SkPaint());
|
||||
canvas.drawBitmap(bitmap, 10, 10, nullptr);
|
||||
});
|
||||
OpReorderer reorderer(100, 200, *dl, sLightCenter);
|
||||
OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
|
||||
createSyncedNodeList(node), sLightCenter);
|
||||
SimpleTestRenderer renderer;
|
||||
reorderer.replayBakedOps<TestDispatcher>(renderer);
|
||||
EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end
|
||||
}
|
||||
|
||||
TEST(OpReorderer, simpleRejection) {
|
||||
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
|
||||
auto node = TestUtils::createNode(0, 0, 200, 200,
|
||||
[](RenderProperties& props, RecordingCanvas& canvas) {
|
||||
canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
|
||||
canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op); // intersection should be empty
|
||||
canvas.drawRect(0, 0, 400, 400, SkPaint());
|
||||
canvas.restore();
|
||||
});
|
||||
OpReorderer reorderer(200, 200, *dl, sLightCenter);
|
||||
OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
|
||||
createSyncedNodeList(node), sLightCenter);
|
||||
|
||||
FailRenderer renderer;
|
||||
reorderer.replayBakedOps<TestDispatcher>(renderer);
|
||||
@@ -147,7 +151,8 @@ TEST(OpReorderer, simpleBatching) {
|
||||
}
|
||||
};
|
||||
|
||||
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
|
||||
auto node = TestUtils::createNode(0, 0, 200, 200,
|
||||
[](RenderProperties& props, RecordingCanvas& canvas) {
|
||||
SkBitmap bitmap = TestUtils::createSkBitmap(10, 10);
|
||||
|
||||
// Alternate between drawing rects and bitmaps, with bitmaps overlapping rects.
|
||||
@@ -161,7 +166,8 @@ TEST(OpReorderer, simpleBatching) {
|
||||
canvas.restore();
|
||||
});
|
||||
|
||||
OpReorderer reorderer(200, 200, *dl, sLightCenter);
|
||||
OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
|
||||
createSyncedNodeList(node), sLightCenter);
|
||||
SimpleBatchingTestRenderer renderer;
|
||||
reorderer.replayBakedOps<TestDispatcher>(renderer);
|
||||
EXPECT_EQ(2 * LOOPS, renderer.getIndex())
|
||||
@@ -179,7 +185,8 @@ TEST(OpReorderer, textStrikethroughBatching) {
|
||||
EXPECT_TRUE(mIndex++ < LOOPS) << "Text should be beneath all strikethrough rects";
|
||||
}
|
||||
};
|
||||
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 2000, [](RecordingCanvas& canvas) {
|
||||
auto node = TestUtils::createNode(0, 0, 200, 2000,
|
||||
[](RenderProperties& props, RecordingCanvas& canvas) {
|
||||
SkPaint textPaint;
|
||||
textPaint.setAntiAlias(true);
|
||||
textPaint.setTextSize(20);
|
||||
@@ -189,7 +196,8 @@ TEST(OpReorderer, textStrikethroughBatching) {
|
||||
TestUtils::drawTextToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1));
|
||||
}
|
||||
});
|
||||
OpReorderer reorderer(200, 2000, *dl, sLightCenter);
|
||||
OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 2000), 200, 2000,
|
||||
createSyncedNodeList(node), sLightCenter);
|
||||
TextStrikethroughTestRenderer renderer;
|
||||
reorderer.replayBakedOps<TestDispatcher>(renderer);
|
||||
EXPECT_EQ(2 * LOOPS, renderer.getIndex())
|
||||
@@ -215,14 +223,16 @@ TEST(OpReorderer, renderNode) {
|
||||
}
|
||||
};
|
||||
|
||||
sp<RenderNode> child = TestUtils::createNode(10, 10, 110, 110, [](RecordingCanvas& canvas) {
|
||||
auto child = TestUtils::createNode(10, 10, 110, 110,
|
||||
[](RenderProperties& props, RecordingCanvas& canvas) {
|
||||
SkPaint paint;
|
||||
paint.setColor(SK_ColorWHITE);
|
||||
canvas.drawRect(0, 0, 100, 100, paint);
|
||||
});
|
||||
|
||||
RenderNode* childPtr = child.get();
|
||||
sp<RenderNode> parent = TestUtils::createNode(0, 0, 200, 200, [childPtr](RecordingCanvas& canvas) {
|
||||
auto parent = TestUtils::createNode(0, 0, 200, 200,
|
||||
[childPtr](RenderProperties& props, RecordingCanvas& canvas) {
|
||||
SkPaint paint;
|
||||
paint.setColor(SK_ColorDKGRAY);
|
||||
canvas.drawRect(0, 0, 200, 200, paint);
|
||||
@@ -250,7 +260,8 @@ TEST(OpReorderer, clipped) {
|
||||
}
|
||||
};
|
||||
|
||||
sp<RenderNode> node = TestUtils::createNode(0, 0, 200, 200, [](RecordingCanvas& canvas) {
|
||||
auto node = TestUtils::createNode(0, 0, 200, 200,
|
||||
[](RenderProperties& props, RecordingCanvas& canvas) {
|
||||
SkBitmap bitmap = TestUtils::createSkBitmap(200, 200);
|
||||
canvas.drawBitmap(bitmap, 0, 0, nullptr);
|
||||
});
|
||||
@@ -292,13 +303,14 @@ TEST(OpReorderer, saveLayerSimple) {
|
||||
}
|
||||
};
|
||||
|
||||
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
|
||||
auto node = TestUtils::createNode(0, 0, 200, 200,
|
||||
[](RenderProperties& props, RecordingCanvas& canvas) {
|
||||
canvas.saveLayerAlpha(10, 10, 190, 190, 128, SkCanvas::kClipToLayer_SaveFlag);
|
||||
canvas.drawRect(10, 10, 190, 190, SkPaint());
|
||||
canvas.restore();
|
||||
});
|
||||
|
||||
OpReorderer reorderer(200, 200, *dl, sLightCenter);
|
||||
OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
|
||||
createSyncedNodeList(node), sLightCenter);
|
||||
SaveLayerSimpleTestRenderer renderer;
|
||||
reorderer.replayBakedOps<TestDispatcher>(renderer);
|
||||
EXPECT_EQ(4, renderer.getIndex());
|
||||
@@ -355,7 +367,8 @@ TEST(OpReorderer, saveLayerNested) {
|
||||
}
|
||||
};
|
||||
|
||||
auto dl = TestUtils::createDisplayList<RecordingCanvas>(800, 800, [](RecordingCanvas& canvas) {
|
||||
auto node = TestUtils::createNode(0, 0, 800, 800,
|
||||
[](RenderProperties& props, RecordingCanvas& canvas) {
|
||||
canvas.saveLayerAlpha(0, 0, 800, 800, 128, SkCanvas::kClipToLayer_SaveFlag);
|
||||
{
|
||||
canvas.drawRect(0, 0, 800, 800, SkPaint());
|
||||
@@ -368,14 +381,16 @@ TEST(OpReorderer, saveLayerNested) {
|
||||
canvas.restore();
|
||||
});
|
||||
|
||||
OpReorderer reorderer(800, 800, *dl, sLightCenter);
|
||||
OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800,
|
||||
createSyncedNodeList(node), sLightCenter);
|
||||
SaveLayerNestedTestRenderer renderer;
|
||||
reorderer.replayBakedOps<TestDispatcher>(renderer);
|
||||
EXPECT_EQ(10, renderer.getIndex());
|
||||
}
|
||||
|
||||
TEST(OpReorderer, saveLayerContentRejection) {
|
||||
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
|
||||
auto node = TestUtils::createNode(0, 0, 200, 200,
|
||||
[](RenderProperties& props, RecordingCanvas& canvas) {
|
||||
canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
|
||||
canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op);
|
||||
canvas.saveLayerAlpha(200, 200, 400, 400, 128, SkCanvas::kClipToLayer_SaveFlag);
|
||||
@@ -386,7 +401,8 @@ TEST(OpReorderer, saveLayerContentRejection) {
|
||||
canvas.restore();
|
||||
canvas.restore();
|
||||
});
|
||||
OpReorderer reorderer(200, 200, *dl, sLightCenter);
|
||||
OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
|
||||
createSyncedNodeList(node), sLightCenter);
|
||||
|
||||
FailRenderer renderer;
|
||||
// should see no ops, even within the layer, since the layer should be rejected
|
||||
@@ -425,7 +441,7 @@ RENDERTHREAD_TEST(OpReorderer, hwLayerSimple) {
|
||||
}
|
||||
};
|
||||
|
||||
sp<RenderNode> node = TestUtils::createNode(10, 10, 110, 110,
|
||||
auto node = TestUtils::createNode(10, 10, 110, 110,
|
||||
[](RenderProperties& props, RecordingCanvas& canvas) {
|
||||
props.mutateLayerProperties().setType(LayerType::RenderLayer);
|
||||
SkPaint paint;
|
||||
@@ -563,7 +579,7 @@ static void drawOrderedRect(RecordingCanvas* canvas, uint8_t expectedDrawOrder)
|
||||
}
|
||||
static void drawOrderedNode(RecordingCanvas* canvas, uint8_t expectedDrawOrder, float z) {
|
||||
auto node = TestUtils::createNode(0, 0, 100, 100,
|
||||
[expectedDrawOrder](RecordingCanvas& canvas) {
|
||||
[expectedDrawOrder](RenderProperties& props, RecordingCanvas& canvas) {
|
||||
drawOrderedRect(&canvas, expectedDrawOrder);
|
||||
});
|
||||
node->mutateStagingProperties().setTranslationZ(z);
|
||||
@@ -580,7 +596,7 @@ TEST(OpReorderer, zReorder) {
|
||||
};
|
||||
|
||||
auto parent = TestUtils::createNode(0, 0, 100, 100,
|
||||
[](RecordingCanvas& canvas) {
|
||||
[](RenderProperties& props, RecordingCanvas& canvas) {
|
||||
drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder
|
||||
drawOrderedRect(&canvas, 1);
|
||||
canvas.insertReorderBarrier(true);
|
||||
@@ -601,10 +617,93 @@ TEST(OpReorderer, zReorder) {
|
||||
EXPECT_EQ(10, renderer.getIndex());
|
||||
};
|
||||
|
||||
TEST(OpReorderer, projectionReorder) {
|
||||
static const int scrollX = 5;
|
||||
static const int scrollY = 10;
|
||||
class ProjectionReorderTestRenderer : public TestRendererBase {
|
||||
public:
|
||||
void onRectOp(const RectOp& op, const BakedOpState& state) override {
|
||||
const int index = mIndex++;
|
||||
|
||||
Matrix4 expectedMatrix;
|
||||
switch (index) {
|
||||
case 0:
|
||||
EXPECT_EQ(Rect(100, 100), op.unmappedBounds);
|
||||
EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
|
||||
expectedMatrix.loadIdentity();
|
||||
break;
|
||||
case 1:
|
||||
EXPECT_EQ(Rect(-10, -10, 60, 60), op.unmappedBounds);
|
||||
EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
|
||||
expectedMatrix.loadTranslate(50, 50, 0); // TODO: should scroll be respected here?
|
||||
break;
|
||||
case 2:
|
||||
EXPECT_EQ(Rect(100, 50), op.unmappedBounds);
|
||||
EXPECT_EQ(SK_ColorBLUE, op.paint->getColor());
|
||||
expectedMatrix.loadTranslate(-scrollX, 50 - scrollY, 0);
|
||||
break;
|
||||
default:
|
||||
ADD_FAILURE();
|
||||
}
|
||||
EXPECT_MATRIX_APPROX_EQ(expectedMatrix, state.computedState.transform);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C)
|
||||
* with a projecting child (P) of its own. P would normally draw between B and C's "background"
|
||||
* draw, but because it is projected backwards, it's drawn in between B and C.
|
||||
*
|
||||
* The parent is scrolled by scrollX/scrollY, but this does not affect the background
|
||||
* (which isn't affected by scroll).
|
||||
*/
|
||||
auto receiverBackground = TestUtils::createNode(0, 0, 100, 100,
|
||||
[](RenderProperties& properties, RecordingCanvas& canvas) {
|
||||
properties.setProjectionReceiver(true);
|
||||
// scroll doesn't apply to background, so undone via translationX/Y
|
||||
// NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
|
||||
properties.setTranslationX(scrollX);
|
||||
properties.setTranslationY(scrollY);
|
||||
|
||||
SkPaint paint;
|
||||
paint.setColor(SK_ColorWHITE);
|
||||
canvas.drawRect(0, 0, 100, 100, paint);
|
||||
});
|
||||
auto projectingRipple = TestUtils::createNode(50, 0, 100, 50,
|
||||
[](RenderProperties& properties, RecordingCanvas& canvas) {
|
||||
properties.setProjectBackwards(true);
|
||||
properties.setClipToBounds(false);
|
||||
SkPaint paint;
|
||||
paint.setColor(SK_ColorDKGRAY);
|
||||
canvas.drawRect(-10, -10, 60, 60, paint);
|
||||
});
|
||||
auto child = TestUtils::createNode(0, 50, 100, 100,
|
||||
[&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
|
||||
SkPaint paint;
|
||||
paint.setColor(SK_ColorBLUE);
|
||||
canvas.drawRect(0, 0, 100, 50, paint);
|
||||
canvas.drawRenderNode(projectingRipple.get());
|
||||
});
|
||||
auto parent = TestUtils::createNode(0, 0, 100, 100,
|
||||
[&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
|
||||
canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
|
||||
canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
|
||||
canvas.drawRenderNode(receiverBackground.get());
|
||||
canvas.drawRenderNode(child.get());
|
||||
canvas.restore();
|
||||
});
|
||||
|
||||
OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
|
||||
createSyncedNodeList(parent), sLightCenter);
|
||||
ProjectionReorderTestRenderer renderer;
|
||||
reorderer.replayBakedOps<TestDispatcher>(renderer);
|
||||
EXPECT_EQ(3, renderer.getIndex());
|
||||
}
|
||||
|
||||
// creates a 100x100 shadow casting node with provided translationZ
|
||||
static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) {
|
||||
return TestUtils::createNode(0, 0, 100, 100,
|
||||
[translationZ] (RenderProperties& properties, RecordingCanvas& canvas) {
|
||||
[translationZ](RenderProperties& properties, RecordingCanvas& canvas) {
|
||||
properties.setTranslationZ(translationZ);
|
||||
properties.mutableOutline().setRoundRect(0, 0, 100, 100, 0.0f, 1.0f);
|
||||
SkPaint paint;
|
||||
@@ -631,8 +730,8 @@ TEST(OpReorderer, shadow) {
|
||||
}
|
||||
};
|
||||
|
||||
sp<RenderNode> parent = TestUtils::createNode(0, 0, 200, 200,
|
||||
[] (RecordingCanvas& canvas) {
|
||||
auto parent = TestUtils::createNode(0, 0, 200, 200,
|
||||
[](RenderProperties& props, RecordingCanvas& canvas) {
|
||||
canvas.insertReorderBarrier(true);
|
||||
canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
|
||||
});
|
||||
@@ -667,8 +766,8 @@ TEST(OpReorderer, shadowSaveLayer) {
|
||||
}
|
||||
};
|
||||
|
||||
sp<RenderNode> parent = TestUtils::createNode(0, 0, 200, 200,
|
||||
[] (RecordingCanvas& canvas) {
|
||||
auto parent = TestUtils::createNode(0, 0, 200, 200,
|
||||
[](RenderProperties& props, RecordingCanvas& canvas) {
|
||||
// save/restore outside of reorderBarrier, so they don't get moved out of place
|
||||
canvas.translate(20, 10);
|
||||
int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SkCanvas::kClipToLayer_SaveFlag);
|
||||
@@ -707,7 +806,7 @@ RENDERTHREAD_TEST(OpReorderer, shadowHwLayer) {
|
||||
}
|
||||
};
|
||||
|
||||
sp<RenderNode> parent = TestUtils::createNode(50, 60, 150, 160,
|
||||
auto parent = TestUtils::createNode(50, 60, 150, 160,
|
||||
[](RenderProperties& props, RecordingCanvas& canvas) {
|
||||
props.mutateLayerProperties().setType(LayerType::RenderLayer);
|
||||
canvas.insertReorderBarrier(true);
|
||||
@@ -750,8 +849,8 @@ TEST(OpReorderer, shadowLayering) {
|
||||
EXPECT_TRUE(index == 2 || index == 3);
|
||||
}
|
||||
};
|
||||
sp<RenderNode> parent = TestUtils::createNode(0, 0, 200, 200,
|
||||
[] (RecordingCanvas& canvas) {
|
||||
auto parent = TestUtils::createNode(0, 0, 200, 200,
|
||||
[](RenderProperties& props, RecordingCanvas& canvas) {
|
||||
canvas.insertReorderBarrier(true);
|
||||
canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
|
||||
canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get());
|
||||
@@ -955,7 +1054,7 @@ TEST(OpReorderer, renderPropSaveLayerAlphaRotate) {
|
||||
SaveLayerAlphaData observedData;
|
||||
testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
|
||||
// Translate and rotate the view so that the only visible part is the top left corner of
|
||||
// the view. It will form an isoceles right triangle with a long side length of 200 at the
|
||||
// the view. It will form an isosceles right triangle with a long side length of 200 at the
|
||||
// bottom of the viewport.
|
||||
properties.setTranslationX(100);
|
||||
properties.setTranslationY(100);
|
||||
|
||||
@@ -321,6 +321,49 @@ TEST(RecordingCanvas, saveLayer_rotateClipped) {
|
||||
EXPECT_EQ(3, count);
|
||||
}
|
||||
|
||||
TEST(RecordingCanvas, drawRenderNode_projection) {
|
||||
sp<RenderNode> background = TestUtils::createNode(50, 50, 150, 150,
|
||||
[](RenderProperties& props, RecordingCanvas& canvas) {
|
||||
SkPaint paint;
|
||||
paint.setColor(SK_ColorWHITE);
|
||||
canvas.drawRect(0, 0, 100, 100, paint);
|
||||
});
|
||||
{
|
||||
background->mutateStagingProperties().setProjectionReceiver(false);
|
||||
|
||||
// NO RECEIVER PRESENT
|
||||
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200,
|
||||
[&background](RecordingCanvas& canvas) {
|
||||
canvas.drawRect(0, 0, 100, 100, SkPaint());
|
||||
canvas.drawRenderNode(background.get());
|
||||
canvas.drawRect(0, 0, 100, 100, SkPaint());
|
||||
});
|
||||
EXPECT_EQ(-1, dl->projectionReceiveIndex)
|
||||
<< "no projection receiver should have been observed";
|
||||
}
|
||||
{
|
||||
background->mutateStagingProperties().setProjectionReceiver(true);
|
||||
|
||||
// RECEIVER PRESENT
|
||||
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200,
|
||||
[&background](RecordingCanvas& canvas) {
|
||||
canvas.drawRect(0, 0, 100, 100, SkPaint());
|
||||
canvas.drawRenderNode(background.get());
|
||||
canvas.drawRect(0, 0, 100, 100, SkPaint());
|
||||
});
|
||||
|
||||
ASSERT_EQ(3u, dl->getOps().size()) << "Must be three ops";
|
||||
auto op = dl->getOps()[1];
|
||||
EXPECT_EQ(RecordedOpId::RenderNodeOp, op->opId);
|
||||
EXPECT_EQ(1, dl->projectionReceiveIndex)
|
||||
<< "correct projection receiver not identified";
|
||||
|
||||
// verify the behavior works even though projection receiver hasn't been sync'd yet
|
||||
EXPECT_TRUE(background->stagingProperties().isProjectionReceiver());
|
||||
EXPECT_FALSE(background->properties().isProjectionReceiver());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(RecordingCanvas, insertReorderBarrier) {
|
||||
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
|
||||
canvas.drawRect(0, 0, 400, 400, SkPaint());
|
||||
|
||||
@@ -121,7 +121,7 @@ public:
|
||||
}
|
||||
|
||||
static sp<RenderNode> createNode(int left, int top, int right, int bottom,
|
||||
std::function<void(RenderProperties& props, TestCanvas& canvas)> setup = nullptr) {
|
||||
std::function<void(RenderProperties& props, TestCanvas& canvas)> setup) {
|
||||
#if HWUI_NULL_GPU
|
||||
// if RenderNodes are being sync'd/used, device info will be needed, since
|
||||
// DeviceInfo::maxTextureSize() affects layer property
|
||||
@@ -140,22 +140,6 @@ public:
|
||||
return node;
|
||||
}
|
||||
|
||||
static sp<RenderNode> createNode(int left, int top, int right, int bottom,
|
||||
std::function<void(RenderProperties& props)> setup) {
|
||||
return createNode(left, top, right, bottom,
|
||||
[&setup](RenderProperties& props, TestCanvas& canvas) {
|
||||
setup(props);
|
||||
});
|
||||
}
|
||||
|
||||
static sp<RenderNode> createNode(int left, int top, int right, int bottom,
|
||||
std::function<void(TestCanvas& canvas)> setup) {
|
||||
return createNode(left, top, right, bottom,
|
||||
[&setup](RenderProperties& props, TestCanvas& canvas) {
|
||||
setup(canvas);
|
||||
});
|
||||
}
|
||||
|
||||
static void recordNode(RenderNode& node,
|
||||
std::function<void(TestCanvas&)> contentCallback) {
|
||||
TestCanvas canvas(node.stagingProperties().getWidth(),
|
||||
@@ -164,6 +148,13 @@ public:
|
||||
node.setStagingDisplayList(canvas.finishRecording());
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces a sync of a tree of RenderNode, such that every descendant will have its staging
|
||||
* properties and DisplayList moved to the render copies.
|
||||
*
|
||||
* Note: does not check dirtiness bits, so any non-staging DisplayLists will be discarded.
|
||||
* For this reason, this should generally only be called once on a tree.
|
||||
*/
|
||||
static void syncHierarchyPropertiesAndDisplayList(sp<RenderNode>& node) {
|
||||
syncHierarchyPropertiesAndDisplayListImpl(node.get());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user