Initial commit of new Canvas operation recording / replay
Done:
- drawRect, drawBitmap, drawColor, drawPaint, drawRenderNode, drawRegion
- Recording with new DisplayList format
- batching & reordering
- Stateless op reorder
- Stateless op rendering
- Frame lifecycle (clear, geterror, cleanup)
Not done:
- SaveLayer (clipped and unclipped)
- HW layers
- Complex clipping
- Ripple projection
- Z reordering
- Z shadows
- onDefer prefetching (text + task kickoff)
- round rect clip
- linear allocation for std collections
- AssetAtlas support
Change-Id: Iaf98c1a3aeab5fa47cc8f9c6d964420abc0e7691
diff --git a/libs/hwui/BakedOpState.h b/libs/hwui/BakedOpState.h
new file mode 100644
index 0000000..e2201ca
--- /dev/null
+++ b/libs/hwui/BakedOpState.h
@@ -0,0 +1,142 @@
+/*
+ * 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_BAKED_OP_STATE_H
+#define ANDROID_HWUI_BAKED_OP_STATE_H
+
+#include "Matrix.h"
+#include "RecordedOp.h"
+#include "Rect.h"
+#include "Snapshot.h"
+
+namespace android {
+namespace uirenderer {
+
+namespace OpClipSideFlags {
+ enum {
+ None = 0x0,
+ Left = 0x1,
+ Top = 0x2,
+ Right = 0x4,
+ Bottom = 0x8,
+ Full = 0xF,
+ // ConservativeFull = 0x1F needed?
+ };
+}
+
+/**
+ * Holds the resolved clip, transform, and bounds of a recordedOp, when replayed with a snapshot
+ */
+class ResolvedRenderState {
+public:
+ // TODO: remove the mapRects/matrix multiply when snapshot & recorded transforms are translates
+ ResolvedRenderState(const Snapshot& snapshot, const RecordedOp& recordedOp) {
+ /* TODO: benchmark a fast path for translate-only matrices, such as:
+ if (CC_LIKELY(snapshot.transform->getType() == Matrix4::kTypeTranslate
+ && recordedOp.localMatrix.getType() == Matrix4::kTypeTranslate)) {
+ float translateX = snapshot.transform->getTranslateX() + recordedOp.localMatrix.getTranslateX();
+ float translateY = snapshot.transform->getTranslateY() + recordedOp.localMatrix.getTranslateY();
+ transform.loadTranslate(translateX, translateY, 0);
+
+ // resolvedClipRect = intersect(parentMatrix * localClip, parentClip)
+ clipRect = recordedOp.localClipRect;
+ clipRect.translate(translateX, translateY);
+ clipRect.doIntersect(snapshot.getClipRect());
+ clipRect.snapToPixelBoundaries();
+
+ // resolvedClippedBounds = intersect(resolvedMatrix * opBounds, resolvedClipRect)
+ clippedBounds = recordedOp.unmappedBounds;
+ clippedBounds.translate(translateX, translateY);
+ } ... */
+
+ // resolvedMatrix = parentMatrix * localMatrix
+ transform.loadMultiply(*snapshot.transform, recordedOp.localMatrix);
+
+ // resolvedClipRect = intersect(parentMatrix * localClip, parentClip)
+ clipRect = recordedOp.localClipRect;
+ snapshot.transform->mapRect(clipRect);
+ clipRect.doIntersect(snapshot.getClipRect());
+ clipRect.snapToPixelBoundaries();
+
+ // resolvedClippedBounds = intersect(resolvedMatrix * opBounds, resolvedClipRect)
+ clippedBounds = recordedOp.unmappedBounds;
+ transform.mapRect(clippedBounds);
+
+ if (clipRect.left > clippedBounds.left) clipSideFlags |= OpClipSideFlags::Left;
+ if (clipRect.top > clippedBounds.top) clipSideFlags |= OpClipSideFlags::Top;
+ if (clipRect.right < clippedBounds.right) clipSideFlags |= OpClipSideFlags::Right;
+ if (clipRect.bottom < clippedBounds.bottom) clipSideFlags |= OpClipSideFlags::Bottom;
+ clippedBounds.doIntersect(clipRect);
+
+ /**
+ * TODO: once we support complex clips, we may want to reject to avoid that work where
+ * possible. Should we:
+ * 1 - quickreject based on clippedBounds, quick early (duplicating logic in resolvedOp)
+ * 2 - merge stuff into tryConstruct factory method, so it can handle quickRejection
+ * and early return null in one place.
+ */
+ }
+ Matrix4 transform;
+ Rect clipRect;
+ int clipSideFlags = 0;
+ Rect clippedBounds;
+};
+
+/**
+ * Self-contained op wrapper, containing all resolved state required to draw the op.
+ *
+ * Stashed pointers within all point to longer lived objects, with no ownership implied.
+ */
+class BakedOpState {
+public:
+ static BakedOpState* tryConstruct(LinearAllocator& allocator,
+ const Snapshot& snapshot, const RecordedOp& recordedOp) {
+ BakedOpState* bakedOp = new (allocator) BakedOpState(
+ snapshot, recordedOp);
+ if (bakedOp->computedState.clippedBounds.isEmpty()) {
+ // bounds are empty, so op is rejected
+ allocator.rewindIfLastAlloc(bakedOp);
+ return nullptr;
+ }
+ return bakedOp;
+ }
+
+ static void* operator new(size_t size, LinearAllocator& allocator) {
+ return allocator.alloc(size);
+ }
+
+ // computed state:
+ const ResolvedRenderState computedState;
+
+ // simple state (straight pointer/value storage):
+ const float alpha;
+ const RoundRectClipState* roundRectClipState;
+ const ProjectionPathMask* projectionPathMask;
+ const RecordedOp* op;
+
+private:
+ BakedOpState(const Snapshot& snapshot, const RecordedOp& recordedOp)
+ : computedState(snapshot, recordedOp)
+ , alpha(snapshot.alpha)
+ , roundRectClipState(snapshot.roundRectClipState)
+ , projectionPathMask(snapshot.projectionPathMask)
+ , op(&recordedOp) {}
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_BAKED_OP_STATE_H