Add z-reordering support to OpReorderer
Change-Id: I3fa969fe53cf648d145810f69fa7dada376c0b9a
diff --git a/libs/hwui/OpReorderer.cpp b/libs/hwui/OpReorderer.cpp
index c6b1609..68f80ea 100644
--- a/libs/hwui/OpReorderer.cpp
+++ b/libs/hwui/OpReorderer.cpp
@@ -16,12 +16,14 @@
#include "OpReorderer.h"
-#include "utils/PaintUtils.h"
-#include "RenderNode.h"
#include "LayerUpdateQueue.h"
+#include "RenderNode.h"
+#include "utils/FatVector.h"
+#include "utils/PaintUtils.h"
-#include "SkCanvas.h"
-#include "utils/Trace.h"
+#include <SkCanvas.h>
+#include <utils/Trace.h>
+#include <utils/TypeHelpers.h>
namespace android {
namespace uirenderer {
@@ -375,6 +377,93 @@
}
}
+typedef key_value_pair_t<float, const RenderNodeOp*> ZRenderNodeOpPair;
+
+template <typename V>
+static void buildZSortedChildList(V* zTranslatedNodes,
+ const DisplayList& displayList, const DisplayList::Chunk& chunk) {
+ if (chunk.beginChildIndex == chunk.endChildIndex) return;
+
+ for (size_t i = chunk.beginChildIndex; i < chunk.endChildIndex; i++) {
+ RenderNodeOp* childOp = displayList.getChildren()[i];
+ RenderNode* child = childOp->renderNode;
+ float childZ = child->properties().getZ();
+
+ if (!MathUtils::isZero(childZ) && chunk.reorderChildren) {
+ zTranslatedNodes->push_back(ZRenderNodeOpPair(childZ, childOp));
+ childOp->skipInOrderDraw = true;
+ } else if (!child->properties().getProjectBackwards()) {
+ // regular, in order drawing DisplayList
+ childOp->skipInOrderDraw = false;
+ }
+ }
+
+ // Z sort any 3d children (stable-ness makes z compare fall back to standard drawing order)
+ std::stable_sort(zTranslatedNodes->begin(), zTranslatedNodes->end());
+}
+
+template <typename V>
+static size_t findNonNegativeIndex(const V& zTranslatedNodes) {
+ for (size_t i = 0; i < zTranslatedNodes.size(); i++) {
+ if (zTranslatedNodes[i].key >= 0.0f) return i;
+ }
+ return zTranslatedNodes.size();
+}
+
+template <typename V>
+void OpReorderer::defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedNodes) {
+ const int size = zTranslatedNodes.size();
+ if (size == 0
+ || (mode == ChildrenSelectMode::Negative&& zTranslatedNodes[0].key > 0.0f)
+ || (mode == ChildrenSelectMode::Positive && zTranslatedNodes[size - 1].key < 0.0f)) {
+ // no 3d children to draw
+ return;
+ }
+
+ /**
+ * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
+ * with very similar Z heights to draw together.
+ *
+ * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are
+ * underneath both, and neither's shadow is drawn on top of the other.
+ */
+ const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes);
+ size_t drawIndex, shadowIndex, endIndex;
+ if (mode == ChildrenSelectMode::Negative) {
+ drawIndex = 0;
+ endIndex = nonNegativeIndex;
+ shadowIndex = endIndex; // draw no shadows
+ } else {
+ drawIndex = nonNegativeIndex;
+ endIndex = size;
+ shadowIndex = drawIndex; // potentially draw shadow for each pos Z child
+ }
+
+ float lastCasterZ = 0.0f;
+ while (shadowIndex < endIndex || drawIndex < endIndex) {
+ if (shadowIndex < endIndex) {
+ const RenderNodeOp* casterNodeOp = zTranslatedNodes[shadowIndex].value;
+ const float casterZ = zTranslatedNodes[shadowIndex].key;
+ // 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 < 0.1f) {
+ deferShadow(*casterNodeOp);
+
+ lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
+ shadowIndex++;
+ continue;
+ }
+ }
+
+ const RenderNodeOp* childOp = zTranslatedNodes[drawIndex].value;
+ deferRenderNodeOp(*childOp);
+ drawIndex++;
+ }
+}
+
+void OpReorderer::deferShadow(const RenderNodeOp& casterNodeOp) {
+ // TODO
+}
/**
* Used to define a list of lambdas referencing private OpReorderer::onXXXXOp() methods.
*
@@ -388,17 +477,20 @@
MAP_OPS(OP_RECEIVER)
};
for (const DisplayList::Chunk& chunk : displayList.getChunks()) {
+ FatVector<ZRenderNodeOpPair, 16> zTranslatedNodes;
+ buildZSortedChildList(&zTranslatedNodes, displayList, chunk);
+
+ defer3dChildren(ChildrenSelectMode::Negative, zTranslatedNodes);
for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
const RecordedOp* op = displayList.getOps()[opIndex];
receivers[op->opId](*this, *op);
}
+ defer3dChildren(ChildrenSelectMode::Positive, zTranslatedNodes);
}
}
-void OpReorderer::onRenderNodeOp(const RenderNodeOp& op) {
- if (op.renderNode->nothingToDraw()) {
- return;
- }
+void OpReorderer::deferRenderNodeOp(const RenderNodeOp& op) {
+ if (op.renderNode->nothingToDraw()) return;
int count = mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
// apply state from RecordedOp
@@ -412,6 +504,12 @@
mCanvasState.restoreToCount(count);
}
+void OpReorderer::onRenderNodeOp(const RenderNodeOp& op) {
+ if (!op.skipInOrderDraw) {
+ deferRenderNodeOp(op);
+ }
+}
+
static batchid_t tessellatedBatchId(const SkPaint& paint) {
return paint.getPathEffect()
? OpBatchType::AlphaMaskTexture