diff options
37 files changed, 143 insertions, 2659 deletions
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index d716844b11c3..1ad40ec68622 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -210,8 +210,6 @@ cc_defaults { "DamageAccumulator.cpp", "DeferredLayerUpdater.cpp", "DeviceInfo.cpp", - "DisplayList.cpp", - "FrameBuilder.cpp", "FrameInfo.cpp", "FrameInfoVisualizer.cpp", "GlLayer.cpp", @@ -237,7 +235,6 @@ cc_defaults { "Properties.cpp", "PropertyValuesAnimatorSet.cpp", "PropertyValuesHolder.cpp", - "RecordingCanvas.cpp", "RenderNode.cpp", "RenderProperties.cpp", "ResourceCache.cpp", @@ -399,7 +396,6 @@ cc_benchmark { srcs: [ "tests/microbench/main.cpp", "tests/microbench/DisplayListCanvasBench.cpp", - "tests/microbench/FrameBuilderBench.cpp", "tests/microbench/LinearAllocatorBench.cpp", "tests/microbench/PathParserBench.cpp", "tests/microbench/RenderNodeBench.cpp", diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h index 42f4cf8828bd..ed7b6eb1cf4a 100644 --- a/libs/hwui/Animator.h +++ b/libs/hwui/Animator.h @@ -16,6 +16,8 @@ #ifndef ANIMATOR_H #define ANIMATOR_H +#include "CanvasProperty.h" + #include <cutils/compiler.h> #include <utils/RefBase.h> #include <utils/StrongPointer.h> @@ -31,8 +33,6 @@ namespace uirenderer { class AnimationContext; class BaseRenderNodeAnimator; -class CanvasPropertyPrimitive; -class CanvasPropertyPaint; class Interpolator; class RenderNode; class RenderProperties; diff --git a/libs/hwui/BakedOpRenderer.h b/libs/hwui/BakedOpRenderer.h index ae8928ea8461..72c93650a83b 100644 --- a/libs/hwui/BakedOpRenderer.h +++ b/libs/hwui/BakedOpRenderer.h @@ -17,6 +17,7 @@ #pragma once #include "BakedOpState.h" +#include "Lighting.h" #include "Matrix.h" #include "utils/Macros.h" @@ -42,16 +43,6 @@ struct ClipBase; class BakedOpRenderer { public: typedef void (*GlopReceiver)(BakedOpRenderer&, const Rect*, const ClipBase*, const Glop&); - /** - * Position agnostic shadow lighting info. Used with all shadow ops in scene. - */ - struct LightInfo { - LightInfo() : LightInfo(0, 0) {} - LightInfo(uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) - : ambientShadowAlpha(ambientShadowAlpha), spotShadowAlpha(spotShadowAlpha) {} - uint8_t ambientShadowAlpha; - uint8_t spotShadowAlpha; - }; BakedOpRenderer(Caches& caches, RenderState& renderState, bool opaque, bool wideColorGamut, const LightInfo& lightInfo) diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp deleted file mode 100644 index f1f0d2ddfcff..000000000000 --- a/libs/hwui/DisplayList.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C) 2013 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 <SkCanvas.h> -#include <algorithm> - -#include <utils/Trace.h> - -#include "DamageAccumulator.h" -#include "Debug.h" -#include "DisplayList.h" -#include "OpDumper.h" -#include "RecordedOp.h" -#include "RenderNode.h" -#include "VectorDrawable.h" -#include "renderthread/CanvasContext.h" - -namespace android { -namespace uirenderer { - -DisplayList::DisplayList() - : projectionReceiveIndex(-1) - , stdAllocator(allocator) - , chunks(stdAllocator) - , ops(stdAllocator) - , children(stdAllocator) - , bitmapResources(stdAllocator) - , pathResources(stdAllocator) - , patchResources(stdAllocator) - , paints(stdAllocator) - , regions(stdAllocator) - , referenceHolders(stdAllocator) - , functors(stdAllocator) - , vectorDrawables(stdAllocator) {} - -DisplayList::~DisplayList() { - cleanupResources(); -} - -void DisplayList::cleanupResources() { - if (CC_UNLIKELY(patchResources.size())) { - ResourceCache& resourceCache = ResourceCache::getInstance(); - resourceCache.lock(); - - for (size_t i = 0; i < patchResources.size(); i++) { - resourceCache.decrementRefcountLocked(patchResources[i]); - } - - resourceCache.unlock(); - } - - for (size_t i = 0; i < pathResources.size(); i++) { - const SkPath* path = pathResources[i]; - delete path; - } - - for (auto& iter : functors) { - if (iter.listener) { - iter.listener->onGlFunctorReleased(iter.functor); - } - } - - patchResources.clear(); - pathResources.clear(); - paints.clear(); - regions.clear(); -} - -size_t DisplayList::addChild(NodeOpType* op) { - referenceHolders.push_back(op->renderNode); - size_t index = children.size(); - children.push_back(op); - return index; -} - -void DisplayList::syncContents() { - for (auto& iter : functors) { - (*iter.functor)(DrawGlInfo::kModeSync, nullptr); - } - for (auto& vectorDrawable : vectorDrawables) { - vectorDrawable->syncProperties(); - } -} - -void DisplayList::updateChildren(std::function<void(RenderNode*)> updateFn) { - for (auto&& child : children) { - updateFn(child->renderNode); - } -} - -bool DisplayList::prepareListAndChildren( - TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer, - std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn) { - info.prepareTextures = info.canvasContext.pinImages(bitmapResources); - - for (auto&& op : children) { - RenderNode* childNode = op->renderNode; - info.damageAccumulator->pushTransform(&op->localMatrix); - bool childFunctorsNeedLayer = - functorsNeedLayer; // TODO! || op->mRecordedWithPotentialStencilClip; - childFn(childNode, observer, info, childFunctorsNeedLayer); - info.damageAccumulator->popTransform(); - } - - bool isDirty = false; - for (auto& vectorDrawable : vectorDrawables) { - // If any vector drawable in the display list needs update, damage the node. - if (vectorDrawable->isDirty()) { - isDirty = true; - } - vectorDrawable->setPropertyChangeWillBeConsumed(true); - } - return isDirty; -} - -void DisplayList::output(std::ostream& output, uint32_t level) { - for (auto&& op : getOps()) { - OpDumper::dump(*op, output, level + 1); - if (op->opId == RecordedOpId::RenderNodeOp) { - auto rnOp = reinterpret_cast<const RenderNodeOp*>(op); - rnOp->renderNode->output(output, level + 1); - } else { - output << std::endl; - } - } -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h index 7a9c65dd365b..a952cc23e1ef 100644 --- a/libs/hwui/DisplayList.h +++ b/libs/hwui/DisplayList.h @@ -16,149 +16,20 @@ #pragma once -#include <SkCamera.h> -#include <SkDrawable.h> -#include <SkMatrix.h> - -#include <private/hwui/DrawGlInfo.h> - -#include <utils/KeyedVector.h> -#include <utils/LinearAllocator.h> -#include <utils/RefBase.h> -#include <utils/SortedVector.h> -#include <utils/String8.h> - -#include <cutils/compiler.h> - -#include <androidfw/ResourceTypes.h> - -#include "CanvasProperty.h" -#include "Debug.h" -#include "GlFunctorLifecycleListener.h" -#include "Matrix.h" -#include "RenderProperties.h" -#include "TreeInfo.h" -#include "hwui/Bitmap.h" - -#include <vector> - -class SkBitmap; -class SkPaint; -class SkPath; -class SkRegion; +#include "pipeline/skia/SkiaDisplayList.h" namespace android { namespace uirenderer { -struct ClipBase; -class Rect; -class Layer; - -struct RecordedOp; -struct RenderNodeOp; - -typedef RecordedOp BaseOpType; -typedef RenderNodeOp NodeOpType; - namespace VectorDrawable { class Tree; }; typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot; -struct FunctorContainer { - Functor* functor; - GlFunctorLifecycleListener* listener; -}; - /** * Data structure that holds the list of commands used in display list stream */ -class DisplayList { - friend class RecordingCanvas; - -public: - struct Chunk { - // range of included ops in DisplayList::ops() - size_t beginOpIndex; - size_t endOpIndex; - - // range of included children in DisplayList::children() - size_t beginChildIndex; - size_t endChildIndex; - - // whether children with non-zero Z in the chunk should be reordered - bool reorderChildren; - - // clip at the beginning of a reorder section, applied to reordered children - const ClipBase* reorderClip; - }; - - DisplayList(); - virtual ~DisplayList(); - - // index of DisplayListOp restore, after which projected descendants should be drawn - int projectionReceiveIndex; - - const LsaVector<Chunk>& getChunks() const { return chunks; } - const LsaVector<BaseOpType*>& getOps() const { return ops; } - - const LsaVector<NodeOpType*>& getChildren() const { return children; } - - const LsaVector<sk_sp<Bitmap>>& getBitmapResources() const { return bitmapResources; } - - size_t addChild(NodeOpType* childOp); - - void ref(VirtualLightRefBase* prop) { referenceHolders.push_back(prop); } - - size_t getUsedSize() { return allocator.usedSize(); } - - virtual bool isEmpty() const { return ops.empty(); } - virtual bool hasFunctor() const { return !functors.empty(); } - virtual bool hasVectorDrawables() const { return !vectorDrawables.empty(); } - virtual bool isSkiaDL() const { return false; } - virtual bool reuseDisplayList(RenderNode* node, renderthread::CanvasContext* context) { - return false; - } - - virtual void syncContents(); - virtual void updateChildren(std::function<void(RenderNode*)> updateFn); - virtual bool prepareListAndChildren( - TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer, - std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn); - - virtual void output(std::ostream& output, uint32_t level); - -protected: - // allocator into which all ops and LsaVector arrays allocated - LinearAllocator allocator; - LinearStdAllocator<void*> stdAllocator; - -private: - LsaVector<Chunk> chunks; - LsaVector<BaseOpType*> ops; - - // list of Ops referring to RenderNode children for quick, non-drawing traversal - LsaVector<NodeOpType*> children; - - // Resources - Skia objects + 9 patches referred to by this DisplayList - LsaVector<sk_sp<Bitmap>> bitmapResources; - LsaVector<const SkPath*> pathResources; - LsaVector<const Res_png_9patch*> patchResources; - LsaVector<std::unique_ptr<const SkPaint>> paints; - LsaVector<std::unique_ptr<const SkRegion>> regions; - LsaVector<sp<VirtualLightRefBase>> referenceHolders; - - // List of functors - LsaVector<FunctorContainer> functors; - - // List of VectorDrawables that need to be notified of pushStaging. Note that this list gets - // nothing - // but a callback during sync DisplayList, unlike the list of functors defined above, which - // gets special treatment exclusive for webview. - LsaVector<VectorDrawableRoot*> vectorDrawables; - - void cleanupResources(); -}; +using DisplayList = skiapipeline::SkiaDisplayList; }; // namespace uirenderer }; // namespace android diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp deleted file mode 100644 index 575aea524133..000000000000 --- a/libs/hwui/FrameBuilder.cpp +++ /dev/null @@ -1,867 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "FrameBuilder.h" - -#include "DeferredLayerUpdater.h" -#include "LayerUpdateQueue.h" -#include "RenderNode.h" -#include "VectorDrawable.h" -#include "hwui/Canvas.h" -#include "renderstate/OffscreenBufferPool.h" -#include "utils/FatVector.h" -#include "utils/PaintUtils.h" -#include "utils/TraceUtils.h" - -#include <SkPathOps.h> -#include <utils/TypeHelpers.h> - -namespace android { -namespace uirenderer { - -FrameBuilder::FrameBuilder(const SkRect& clip, uint32_t viewportWidth, uint32_t viewportHeight, - const LightGeometry& lightGeometry, Caches& caches) - : mStdAllocator(mAllocator) - , mLayerBuilders(mStdAllocator) - , mLayerStack(mStdAllocator) - , mCanvasState(*this) - , mCaches(caches) - , mLightRadius(lightGeometry.radius) - , mDrawFbo0(true) { - // Prepare to defer Fbo0 - auto fbo0 = mAllocator.create<LayerBuilder>(viewportWidth, viewportHeight, Rect(clip)); - mLayerBuilders.push_back(fbo0); - mLayerStack.push_back(0); - mCanvasState.initializeSaveStack(viewportWidth, viewportHeight, clip.fLeft, clip.fTop, - clip.fRight, clip.fBottom, lightGeometry.center); -} - -FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const LightGeometry& lightGeometry, - Caches& caches) - : mStdAllocator(mAllocator) - , mLayerBuilders(mStdAllocator) - , mLayerStack(mStdAllocator) - , mCanvasState(*this) - , mCaches(caches) - , mLightRadius(lightGeometry.radius) - , mDrawFbo0(false) { - // TODO: remove, with each layer on its own save stack - - // Prepare to defer Fbo0 (which will be empty) - auto fbo0 = mAllocator.create<LayerBuilder>(1, 1, Rect(1, 1)); - mLayerBuilders.push_back(fbo0); - mLayerStack.push_back(0); - mCanvasState.initializeSaveStack(1, 1, 0, 0, 1, 1, lightGeometry.center); - - deferLayers(layers); -} - -void FrameBuilder::deferLayers(const LayerUpdateQueue& layers) { - // 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 (mLayerBuilders are issued to Renderer in reverse) - for (int i = layers.entries().size() - 1; i >= 0; i--) { - RenderNode* layerNode = layers.entries()[i].renderNode.get(); - // only schedule repaint if node still on layer - possible it may have been - // removed during a dropped frame, but layers may still remain scheduled so - // as not to lose info on what portion is damaged - OffscreenBuffer* layer = layerNode->getLayer(); - if (CC_LIKELY(layer)) { - ATRACE_FORMAT("Optimize HW Layer DisplayList %s %ux%u", layerNode->getName(), - layerNode->getWidth(), layerNode->getHeight()); - - Rect layerDamage = layers.entries()[i].damage; - // TODO: ensure layer damage can't be larger than layer - layerDamage.doIntersect(0, 0, layer->viewportWidth, layer->viewportHeight); - layerNode->computeOrdering(); - - // map current light center into RenderNode's coordinate space - Vector3 lightCenter = mCanvasState.currentSnapshot()->getRelativeLightCenter(); - layer->inverseTransformInWindow.mapPoint3d(lightCenter); - - saveForLayer(layerNode->getWidth(), layerNode->getHeight(), 0, 0, layerDamage, - lightCenter, nullptr, layerNode); - - if (layerNode->getDisplayList()) { - deferNodeOps(*layerNode); - } - restoreForLayer(); - } - } -} - -void FrameBuilder::deferRenderNode(RenderNode& renderNode) { - renderNode.computeOrdering(); - - mCanvasState.save(SaveFlags::MatrixClip); - deferNodePropsAndOps(renderNode); - mCanvasState.restore(); -} - -void FrameBuilder::deferRenderNode(float tx, float ty, Rect clipRect, RenderNode& renderNode) { - renderNode.computeOrdering(); - - mCanvasState.save(SaveFlags::MatrixClip); - mCanvasState.translate(tx, ty); - mCanvasState.clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, - SkClipOp::kIntersect); - deferNodePropsAndOps(renderNode); - mCanvasState.restore(); -} - -static Rect nodeBounds(RenderNode& node) { - auto& props = node.properties(); - return Rect(props.getLeft(), props.getTop(), props.getRight(), props.getBottom()); -} - -void FrameBuilder::deferRenderNodeScene(const std::vector<sp<RenderNode> >& nodes, - const Rect& contentDrawBounds) { - if (nodes.size() < 1) return; - if (nodes.size() == 1) { - if (!nodes[0]->nothingToDraw()) { - deferRenderNode(*nodes[0]); - } - return; - } - // It there are multiple render nodes, they are laid out as follows: - // #0 - backdrop (content + caption) - // #1 - content (local bounds are at (0,0), will be translated and clipped to backdrop) - // #2 - additional overlay nodes - // Usually the backdrop cannot be seen since it will be entirely covered by the content. While - // resizing however it might become partially visible. The following render loop will crop the - // backdrop against the content and draw the remaining part of it. It will then draw the content - // cropped to the backdrop (since that indicates a shrinking of the window). - // - // Additional nodes will be drawn on top with no particular clipping semantics. - - // Usually the contents bounds should be mContentDrawBounds - however - we will - // move it towards the fixed edge to give it a more stable appearance (for the moment). - // If there is no content bounds we ignore the layering as stated above and start with 2. - - // Backdrop bounds in render target space - const Rect backdrop = nodeBounds(*nodes[0]); - - // Bounds that content will fill in render target space (note content node bounds may be bigger) - Rect content(contentDrawBounds.getWidth(), contentDrawBounds.getHeight()); - content.translate(backdrop.left, backdrop.top); - if (!content.contains(backdrop) && !nodes[0]->nothingToDraw()) { - // Content doesn't entirely overlap backdrop, so fill around content (right/bottom) - - // Note: in the future, if content doesn't snap to backdrop's left/top, this may need to - // also fill left/top. Currently, both 2up and freeform position content at the top/left of - // the backdrop, so this isn't necessary. - if (content.right < backdrop.right) { - // draw backdrop to right side of content - deferRenderNode(0, 0, - Rect(content.right, backdrop.top, backdrop.right, backdrop.bottom), - *nodes[0]); - } - if (content.bottom < backdrop.bottom) { - // draw backdrop to bottom of content - // Note: bottom fill uses content left/right, to avoid overdrawing left/right fill - deferRenderNode(0, 0, - Rect(content.left, content.bottom, content.right, backdrop.bottom), - *nodes[0]); - } - } - - if (!nodes[1]->nothingToDraw()) { - if (!backdrop.isEmpty()) { - // content node translation to catch up with backdrop - float dx = contentDrawBounds.left - backdrop.left; - float dy = contentDrawBounds.top - backdrop.top; - - Rect contentLocalClip = backdrop; - contentLocalClip.translate(dx, dy); - deferRenderNode(-dx, -dy, contentLocalClip, *nodes[1]); - } else { - deferRenderNode(*nodes[1]); - } - } - - // remaining overlay nodes, simply defer - for (size_t index = 2; index < nodes.size(); index++) { - if (!nodes[index]->nothingToDraw()) { - deferRenderNode(*nodes[index]); - } - } -} - -void FrameBuilder::onViewportInitialized() {} - -void FrameBuilder::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {} - -void FrameBuilder::deferNodePropsAndOps(RenderNode& node) { - const RenderProperties& properties = node.properties(); - const Outline& outline = properties.getOutline(); - if (properties.getAlpha() <= 0 || (outline.getShouldClip() && outline.isEmpty()) || - properties.getScaleX() == 0 || properties.getScaleY() == 0) { - return; // rejected - } - - if (properties.getLeft() != 0 || properties.getTop() != 0) { - mCanvasState.translate(properties.getLeft(), properties.getTop()); - } - if (properties.getStaticMatrix()) { - mCanvasState.concatMatrix(*properties.getStaticMatrix()); - } else if (properties.getAnimationMatrix()) { - mCanvasState.concatMatrix(*properties.getAnimationMatrix()); - } - if (properties.hasTransformMatrix()) { - if (properties.isTransformTranslateOnly()) { - mCanvasState.translate(properties.getTranslationX(), properties.getTranslationY()); - } else { - mCanvasState.concatMatrix(*properties.getTransformMatrix()); - } - } - - const int width = properties.getWidth(); - const int height = properties.getHeight(); - - Rect saveLayerBounds; // will be set to non-empty if saveLayer needed - const bool isLayer = properties.effectiveLayerType() != LayerType::None; - int clipFlags = properties.getClippingFlags(); - if (properties.getAlpha() < 1) { - if (isLayer) { - clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer - } - if (CC_LIKELY(isLayer || !properties.getHasOverlappingRendering())) { - // simply scale rendering content's alpha - mCanvasState.scaleAlpha(properties.getAlpha()); - } else { - // schedule saveLayer by initializing saveLayerBounds - saveLayerBounds.set(0, 0, width, height); - if (clipFlags) { - properties.getClippingRectForFlags(clipFlags, &saveLayerBounds); - clipFlags = 0; // all clipping done by savelayer - } - } - - if (CC_UNLIKELY(ATRACE_ENABLED() && properties.promotedToLayer())) { - // pretend alpha always causes savelayer to warn about - // performance problem affecting old versions - ATRACE_FORMAT("%s alpha caused saveLayer %dx%d", node.getName(), width, height); - } - } - if (clipFlags) { - Rect clipRect; - properties.getClippingRectForFlags(clipFlags, &clipRect); - mCanvasState.clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, - SkClipOp::kIntersect); - } - - if (properties.getRevealClip().willClip()) { - Rect bounds; - properties.getRevealClip().getBounds(&bounds); - mCanvasState.setClippingRoundRect(mAllocator, bounds, - properties.getRevealClip().getRadius()); - } else if (properties.getOutline().willClip()) { - mCanvasState.setClippingOutline(mAllocator, &(properties.getOutline())); - } - - bool quickRejected = mCanvasState.currentSnapshot()->getRenderTargetClip().isEmpty() || - (properties.getClipToBounds() && - mCanvasState.quickRejectConservative(0, 0, width, height)); - if (!quickRejected) { - // not rejected, so defer render as either Layer, or direct (possibly wrapped in saveLayer) - if (node.getLayer()) { - // HW layer - LayerOp* drawLayerOp = mAllocator.create_trivial<LayerOp>(node); - BakedOpState* bakedOpState = tryBakeOpState(*drawLayerOp); - if (bakedOpState) { - // Node's layer already deferred, schedule it to render into parent layer - currentLayer().deferUnmergeableOp(mAllocator, bakedOpState, OpBatchType::Bitmap); - } - } else if (CC_UNLIKELY(!saveLayerBounds.isEmpty())) { - // draw DisplayList contents within temporary, since persisted layer could not be used. - // (temp layers are clipped to viewport, since they don't persist offscreen content) - SkPaint saveLayerPaint; - saveLayerPaint.setAlpha(properties.getAlpha()); - deferBeginLayerOp(*mAllocator.create_trivial<BeginLayerOp>( - saveLayerBounds, Matrix4::identity(), - nullptr, // no record-time clip - need only respect defer-time one - &saveLayerPaint)); - deferNodeOps(node); - deferEndLayerOp(*mAllocator.create_trivial<EndLayerOp>()); - } else { - deferNodeOps(node); - } - } -} - -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 FrameBuilder::defer3dChildren(const ClipBase* reorderClip, 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(reorderClip, *casterNodeOp); - - lastCasterZ = casterZ; // must do this even if current caster not casting a shadow - shadowIndex++; - continue; - } - } - - const RenderNodeOp* childOp = zTranslatedNodes[drawIndex].value; - deferRenderNodeOpImpl(*childOp); - drawIndex++; - } -} - -void FrameBuilder::deferShadow(const ClipBase* reorderClip, const RenderNodeOp& casterNodeOp) { - // DEAD CODE -} - -void FrameBuilder::deferProjectedChildren(const RenderNode& renderNode) { - int count = mCanvasState.save(SaveFlags::MatrixClip); - const SkPath* projectionReceiverOutline = renderNode.properties().getOutline().getPath(); - - SkPath transformedMaskPath; // on stack, since BakedOpState makes a deep copy - if (projectionReceiverOutline) { - // transform the mask for this projector into render target space - // TODO: consider combining both transforms by stashing transform instead of applying - SkMatrix skCurrentTransform; - mCanvasState.currentTransform()->copyTo(skCurrentTransform); - projectionReceiverOutline->transform(skCurrentTransform, &transformedMaskPath); - mCanvasState.setProjectionPathMask(&transformedMaskPath); - } - - for (size_t i = 0; i < renderNode.mProjectedNodes.size(); i++) { - RenderNodeOp* childOp = renderNode.mProjectedNodes[i]; - RenderNode& childNode = *childOp->renderNode; - - // Draw child if it has content, but ignore state in childOp - matrix already applied to - // transformFromCompositingAncestor, and record-time clip is ignored when projecting - if (!childNode.nothingToDraw()) { - int restoreTo = mCanvasState.save(SaveFlags::MatrixClip); - - // Apply transform between ancestor and projected descendant - mCanvasState.concatMatrix(childOp->transformFromCompositingAncestor); - - deferNodePropsAndOps(childNode); - - mCanvasState.restoreToCount(restoreTo); - } - } - mCanvasState.restoreToCount(count); -} - -/** - * Used to define a list of lambdas referencing private FrameBuilder::onXX::defer() 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 FrameBuilder::onBitmapOp(const BitmapOp&) - */ -#define OP_RECEIVER(Type) \ - [](FrameBuilder& frameBuilder, const RecordedOp& op) { \ - frameBuilder.defer##Type(static_cast<const Type&>(op)); \ - }, -void FrameBuilder::deferNodeOps(const RenderNode& renderNode) { - typedef void (*OpDispatcher)(FrameBuilder & frameBuilder, const RecordedOp& op); - static OpDispatcher receivers[] = BUILD_DEFERRABLE_OP_LUT(OP_RECEIVER); - - // can't be null, since DL=null node rejection happens before deferNodePropsAndOps - const DisplayList& displayList = *(renderNode.getDisplayList()); - for (auto& chunk : displayList.getChunks()) { - FatVector<ZRenderNodeOpPair, 16> zTranslatedNodes; - buildZSortedChildList(&zTranslatedNodes, displayList, chunk); - - defer3dChildren(chunk.reorderClip, ChildrenSelectMode::Negative, zTranslatedNodes); - 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(chunk.reorderClip, ChildrenSelectMode::Positive, zTranslatedNodes); - } -} - -void FrameBuilder::deferRenderNodeOpImpl(const RenderNodeOp& op) { - if (op.renderNode->nothingToDraw()) return; - int count = mCanvasState.save(SaveFlags::MatrixClip); - - // apply state from RecordedOp (clip first, since op's clip is transformed by current matrix) - mCanvasState.writableSnapshot()->applyClip(op.localClip, - *mCanvasState.currentSnapshot()->transform); - mCanvasState.concatMatrix(op.localMatrix); - - // then apply state from node properties, and defer ops - deferNodePropsAndOps(*op.renderNode); - - mCanvasState.restoreToCount(count); -} - -void FrameBuilder::deferRenderNodeOp(const RenderNodeOp& op) { - if (!op.skipInOrderDraw) { - deferRenderNodeOpImpl(op); - } -} - -/** - * Defers an unmergeable, strokeable op, accounting correctly - * for paint's style on the bounds being computed. - */ -BakedOpState* FrameBuilder::deferStrokeableOp(const RecordedOp& op, batchid_t batchId, - BakedOpState::StrokeBehavior strokeBehavior, - bool expandForPathTexture) { - // Note: here we account for stroke when baking the op - BakedOpState* bakedState = BakedOpState::tryStrokeableOpConstruct( - mAllocator, *mCanvasState.writableSnapshot(), op, strokeBehavior, expandForPathTexture); - if (!bakedState) return nullptr; // quick rejected - - if (op.opId == RecordedOpId::RectOp && op.paint->getStyle() != SkPaint::kStroke_Style) { - bakedState->setupOpacity(op.paint); - } - - currentLayer().deferUnmergeableOp(mAllocator, bakedState, batchId); - return bakedState; -} - -/** - * Returns batch id for tessellatable shapes, based on paint. Checks to see if path effect/AA will - * be used, since they trigger significantly different rendering paths. - * - * Note: not used for lines/points, since they don't currently support path effects. - */ -static batchid_t tessBatchId(const RecordedOp& op) { - const SkPaint& paint = *(op.paint); - return paint.getPathEffect() - ? OpBatchType::AlphaMaskTexture - : (paint.isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices); -} - -void FrameBuilder::deferArcOp(const ArcOp& op) { - // Pass true below since arcs have a tendency to draw outside their expected bounds within - // their path textures. Passing true makes it more likely that we'll scissor, instead of - // corrupting the frame by drawing outside of clip bounds. - deferStrokeableOp(op, tessBatchId(op), BakedOpState::StrokeBehavior::StyleDefined, true); -} - -static bool hasMergeableClip(const BakedOpState& state) { - return !state.computedState.clipState || - state.computedState.clipState->mode == ClipMode::Rectangle; -} - -void FrameBuilder::deferBitmapOp(const BitmapOp& op) { - BakedOpState* bakedState = tryBakeOpState(op); - if (!bakedState) return; // quick rejected - - if (op.bitmap->isOpaque()) { - bakedState->setupOpacity(op.paint); - } - - // Don't merge non-simply transformed or neg scale ops, SET_TEXTURE doesn't handle rotation - // Don't merge A8 bitmaps - the paint's color isn't compared by mergeId, or in - // MergingDrawBatch::canMergeWith() - if (bakedState->computedState.transform.isSimple() && - bakedState->computedState.transform.positiveScale() && - PaintUtils::getBlendModeDirect(op.paint) == SkBlendMode::kSrcOver && - op.bitmap->colorType() != kAlpha_8_SkColorType && hasMergeableClip(*bakedState)) { - mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.bitmap->getGenerationID()); - currentLayer().deferMergeableOp(mAllocator, bakedState, OpBatchType::Bitmap, mergeId); - } else { - currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap); - } -} - -void FrameBuilder::deferBitmapMeshOp(const BitmapMeshOp& op) { - BakedOpState* bakedState = tryBakeOpState(op); - if (!bakedState) return; // quick rejected - currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap); -} - -void FrameBuilder::deferBitmapRectOp(const BitmapRectOp& op) { - BakedOpState* bakedState = tryBakeOpState(op); - if (!bakedState) return; // quick rejected - currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap); -} - -void FrameBuilder::deferVectorDrawableOp(const VectorDrawableOp& op) { - Bitmap& bitmap = op.vectorDrawable->getBitmapUpdateIfDirty(); - SkPaint* paint = op.vectorDrawable->getPaint(); - const BitmapRectOp* resolvedOp = mAllocator.create_trivial<BitmapRectOp>( - op.unmappedBounds, op.localMatrix, op.localClip, paint, &bitmap, - Rect(bitmap.width(), bitmap.height())); - deferBitmapRectOp(*resolvedOp); -} - -void FrameBuilder::deferCirclePropsOp(const CirclePropsOp& op) { - // allocate a temporary oval op (with mAllocator, so it persists until render), so the - // renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple. - float x = *(op.x); - float y = *(op.y); - float radius = *(op.radius); - Rect unmappedBounds(x - radius, y - radius, x + radius, y + radius); - const OvalOp* resolvedOp = mAllocator.create_trivial<OvalOp>(unmappedBounds, op.localMatrix, - op.localClip, op.paint); - deferOvalOp(*resolvedOp); -} - -void FrameBuilder::deferColorOp(const ColorOp& op) { - BakedOpState* bakedState = tryBakeUnboundedOpState(op); - if (!bakedState) return; // quick rejected - currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Vertices); -} - -void FrameBuilder::deferFunctorOp(const FunctorOp& op) { - BakedOpState* bakedState = tryBakeUnboundedOpState(op); - if (!bakedState) return; // quick rejected - currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Functor); -} - -void FrameBuilder::deferLinesOp(const LinesOp& op) { - batchid_t batch = op.paint->isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices; - deferStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced); -} - -void FrameBuilder::deferOvalOp(const OvalOp& op) { - deferStrokeableOp(op, tessBatchId(op)); -} - -void FrameBuilder::deferPatchOp(const PatchOp& op) { - BakedOpState* bakedState = tryBakeOpState(op); - if (!bakedState) return; // quick rejected - - if (bakedState->computedState.transform.isPureTranslate() && - PaintUtils::getBlendModeDirect(op.paint) == SkBlendMode::kSrcOver && - hasMergeableClip(*bakedState)) { - mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.bitmap->getGenerationID()); - - // Only use the MergedPatch batchId when merged, so Bitmap+Patch don't try to merge together - currentLayer().deferMergeableOp(mAllocator, bakedState, OpBatchType::MergedPatch, mergeId); - } else { - // Use Bitmap batchId since Bitmap+Patch use same shader - currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap); - } -} - -void FrameBuilder::deferPathOp(const PathOp& op) { - /*auto state = */deferStrokeableOp(op, OpBatchType::AlphaMaskTexture); -} - -void FrameBuilder::deferPointsOp(const PointsOp& op) { - batchid_t batch = op.paint->isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices; - deferStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced); -} - -void FrameBuilder::deferRectOp(const RectOp& op) { - deferStrokeableOp(op, tessBatchId(op)); -} - -void FrameBuilder::deferRoundRectOp(const RoundRectOp& op) { - // DEAD CODE -} - -void FrameBuilder::deferRoundRectPropsOp(const RoundRectPropsOp& op) { - // allocate a temporary round rect op (with mAllocator, so it persists until render), so the - // renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple. - const RoundRectOp* resolvedOp = mAllocator.create_trivial<RoundRectOp>( - Rect(*(op.left), *(op.top), *(op.right), *(op.bottom)), op.localMatrix, op.localClip, - op.paint, *op.rx, *op.ry); - deferRoundRectOp(*resolvedOp); -} - -void FrameBuilder::deferSimpleRectsOp(const SimpleRectsOp& op) { - BakedOpState* bakedState = tryBakeOpState(op); - if (!bakedState) return; // quick rejected - currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Vertices); -} - -void FrameBuilder::deferTextOp(const TextOp& op) { - // DEAD CODE -} - -void FrameBuilder::deferTextOnPathOp(const TextOnPathOp& op) { - // DEAD CODE -} - -void FrameBuilder::deferTextureLayerOp(const TextureLayerOp& op) { - GlLayer* layer = static_cast<GlLayer*>(op.layerHandle->backingLayer()); - if (CC_UNLIKELY(!layer || !layer->isRenderable())) return; - - const TextureLayerOp* textureLayerOp = &op; - // Now safe to access transform (which was potentially unready at record time) - if (!layer->getTransform().isIdentity()) { - // non-identity transform present, so 'inject it' into op by copying + replacing matrix - Matrix4 combinedMatrix(op.localMatrix); - combinedMatrix.multiply(layer->getTransform()); - textureLayerOp = mAllocator.create<TextureLayerOp>(op, combinedMatrix); - } - BakedOpState* bakedState = tryBakeOpState(*textureLayerOp); - - if (!bakedState) return; // quick rejected - currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::TextureLayer); -} - -void FrameBuilder::saveForLayer(uint32_t layerWidth, uint32_t layerHeight, float contentTranslateX, - float contentTranslateY, const Rect& repaintRect, - const Vector3& lightCenter, const BeginLayerOp* beginLayerOp, - RenderNode* renderNode) { - mCanvasState.save(SaveFlags::MatrixClip); - mCanvasState.writableSnapshot()->initializeViewport(layerWidth, layerHeight); - mCanvasState.writableSnapshot()->roundRectClipState = nullptr; - mCanvasState.writableSnapshot()->setRelativeLightCenter(lightCenter); - mCanvasState.writableSnapshot()->transform->loadTranslate(contentTranslateX, contentTranslateY, - 0); - mCanvasState.writableSnapshot()->setClip(repaintRect.left, repaintRect.top, repaintRect.right, - repaintRect.bottom); - - // create a new layer repaint, and push its index on the stack - mLayerStack.push_back(mLayerBuilders.size()); - auto newFbo = mAllocator.create<LayerBuilder>(layerWidth, layerHeight, repaintRect, - beginLayerOp, renderNode); - mLayerBuilders.push_back(newFbo); -} - -void FrameBuilder::restoreForLayer() { - // restore canvas, and pop finished layer off of the stack - mCanvasState.restore(); - mLayerStack.pop_back(); -} - -// TODO: defer time rejection (when bounds become empty) + tests -// Option - just skip layers with no bounds at playback + defer? -void FrameBuilder::deferBeginLayerOp(const BeginLayerOp& op) { - uint32_t layerWidth = (uint32_t)op.unmappedBounds.getWidth(); - uint32_t layerHeight = (uint32_t)op.unmappedBounds.getHeight(); - - auto previous = mCanvasState.currentSnapshot(); - Vector3 lightCenter = previous->getRelativeLightCenter(); - - // Combine all transforms used to present saveLayer content: - // parent content transform * canvas transform * bounds offset - Matrix4 contentTransform(*(previous->transform)); - contentTransform.multiply(op.localMatrix); - contentTransform.translate(op.unmappedBounds.left, op.unmappedBounds.top); - - Matrix4 inverseContentTransform; - inverseContentTransform.loadInverse(contentTransform); - - // map the light center into layer-relative space - inverseContentTransform.mapPoint3d(lightCenter); - - // Clip bounds of temporary layer to parent's clip rect, so: - Rect saveLayerBounds(layerWidth, layerHeight); - // 1) transform Rect(width, height) into parent's space - // note: left/top offsets put in contentTransform above - contentTransform.mapRect(saveLayerBounds); - // 2) intersect with parent's clip - saveLayerBounds.doIntersect(previous->getRenderTargetClip()); - // 3) and transform back - inverseContentTransform.mapRect(saveLayerBounds); - saveLayerBounds.doIntersect(Rect(layerWidth, layerHeight)); - saveLayerBounds.roundOut(); - - // if bounds are reduced, will clip the layer's area by reducing required bounds... - layerWidth = saveLayerBounds.getWidth(); - layerHeight = saveLayerBounds.getHeight(); - // ...and shifting drawing content to account for left/top side clipping - float contentTranslateX = -saveLayerBounds.left; - float contentTranslateY = -saveLayerBounds.top; - - saveForLayer(layerWidth, layerHeight, contentTranslateX, contentTranslateY, - Rect(layerWidth, layerHeight), lightCenter, &op, nullptr); -} - -void FrameBuilder::deferEndLayerOp(const EndLayerOp& /* ignored */) { - const BeginLayerOp& beginLayerOp = *currentLayer().beginLayerOp; - int finishedLayerIndex = mLayerStack.back(); - - restoreForLayer(); - - // saveLayer will clip & translate the draw contents, so we need - // to translate the drawLayer by how much the contents was translated - // TODO: Unify this with beginLayerOp so we don't have to calculate this - // twice - uint32_t layerWidth = (uint32_t)beginLayerOp.unmappedBounds.getWidth(); - uint32_t layerHeight = (uint32_t)beginLayerOp.unmappedBounds.getHeight(); - - auto previous = mCanvasState.currentSnapshot(); - Vector3 lightCenter = previous->getRelativeLightCenter(); - - // Combine all transforms used to present saveLayer content: - // parent content transform * canvas transform * bounds offset - Matrix4 contentTransform(*(previous->transform)); - contentTransform.multiply(beginLayerOp.localMatrix); - contentTransform.translate(beginLayerOp.unmappedBounds.left, beginLayerOp.unmappedBounds.top); - - Matrix4 inverseContentTransform; - inverseContentTransform.loadInverse(contentTransform); - - // map the light center into layer-relative space - inverseContentTransform.mapPoint3d(lightCenter); - - // Clip bounds of temporary layer to parent's clip rect, so: - Rect saveLayerBounds(layerWidth, layerHeight); - // 1) transform Rect(width, height) into parent's space - // note: left/top offsets put in contentTransform above - contentTransform.mapRect(saveLayerBounds); - // 2) intersect with parent's clip - saveLayerBounds.doIntersect(previous->getRenderTargetClip()); - // 3) and transform back - inverseContentTransform.mapRect(saveLayerBounds); - saveLayerBounds.doIntersect(Rect(layerWidth, layerHeight)); - saveLayerBounds.roundOut(); - - Matrix4 localMatrix(beginLayerOp.localMatrix); - localMatrix.translate(saveLayerBounds.left, saveLayerBounds.top); - - // 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 - LayerOp* drawLayerOp = mAllocator.create_trivial<LayerOp>( - beginLayerOp.unmappedBounds, localMatrix, beginLayerOp.localClip, beginLayerOp.paint, - &(mLayerBuilders[finishedLayerIndex]->offscreenBuffer)); - 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 { - // Layer won't be drawn - delete its drawing batches to prevent it from doing any work - // TODO: need to prevent any render work from being done - // - create layerop earlier for reject purposes? - mLayerBuilders[finishedLayerIndex]->clear(); - return; - } -} - -void FrameBuilder::deferBeginUnclippedLayerOp(const BeginUnclippedLayerOp& op) { - Matrix4 boundsTransform(*(mCanvasState.currentSnapshot()->transform)); - boundsTransform.multiply(op.localMatrix); - - Rect dstRect(op.unmappedBounds); - boundsTransform.mapRect(dstRect); - dstRect.roundOut(); - dstRect.doIntersect(mCanvasState.currentSnapshot()->getRenderTargetClip()); - - if (dstRect.isEmpty()) { - // Unclipped layer rejected - push a null op, so next EndUnclippedLayerOp is ignored - currentLayer().activeUnclippedSaveLayers.push_back(nullptr); - } else { - // Allocate a holding position for the layer object (copyTo will produce, copyFrom will - // consume) - OffscreenBuffer** layerHandle = mAllocator.create<OffscreenBuffer*>(nullptr); - - /** - * First, defer an operation to copy out the content from the rendertarget into a layer. - */ - auto copyToOp = mAllocator.create_trivial<CopyToLayerOp>(op, layerHandle); - BakedOpState* bakedState = BakedOpState::directConstruct( - mAllocator, &(currentLayer().repaintClip), dstRect, *copyToOp); - currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::CopyToLayer); - - /** - * Defer a clear rect, so that clears from multiple unclipped layers can be drawn - * both 1) simultaneously, and 2) as long after the copyToLayer executes as possible - */ - currentLayer().deferLayerClear(dstRect); - - /** - * And stash an operation to copy that layer back under the rendertarget until - * a balanced EndUnclippedLayerOp is seen - */ - auto copyFromOp = mAllocator.create_trivial<CopyFromLayerOp>(op, layerHandle); - bakedState = BakedOpState::directConstruct(mAllocator, &(currentLayer().repaintClip), - dstRect, *copyFromOp); - currentLayer().activeUnclippedSaveLayers.push_back(bakedState); - } -} - -void FrameBuilder::deferEndUnclippedLayerOp(const EndUnclippedLayerOp& /* ignored */) { - LOG_ALWAYS_FATAL_IF(currentLayer().activeUnclippedSaveLayers.empty(), "no layer to end!"); - - BakedOpState* copyFromLayerOp = currentLayer().activeUnclippedSaveLayers.back(); - currentLayer().activeUnclippedSaveLayers.pop_back(); - if (copyFromLayerOp) { - currentLayer().deferUnmergeableOp(mAllocator, copyFromLayerOp, OpBatchType::CopyFromLayer); - } -} - -void FrameBuilder::finishDefer() { - // DEAD CODE -} - -} // namespace uirenderer -} // namespace android diff --git a/libs/hwui/FrameBuilder.h b/libs/hwui/FrameBuilder.h deleted file mode 100644 index 974daf8a17bb..000000000000 --- a/libs/hwui/FrameBuilder.h +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "BakedOpState.h" -#include "CanvasState.h" -#include "DisplayList.h" -#include "LayerBuilder.h" -#include "RecordedOp.h" -#include "utils/GLUtils.h" - -#include <unordered_map> -#include <vector> - -struct SkRect; - -namespace android { -namespace uirenderer { - -class BakedOpState; -class LayerUpdateQueue; -class OffscreenBuffer; -class Rect; - -/** - * Processes, optimizes, and stores rendering commands from RenderNodes and - * LayerUpdateQueue, building content needed to render a frame. - * - * Resolves final drawing state for each operation (including clip, alpha and matrix), and then - * reorder and merge each op as it is resolved for drawing efficiency. Each layer of content (either - * from the LayerUpdateQueue, or temporary layers created by saveLayer operations in the - * draw stream) will create different reorder contexts, each in its own LayerBuilder. - * - * Then the prepared or 'baked' drawing commands can be issued by calling the templated - * replayBakedOps() function, which will dispatch them (including any created merged op collections) - * to a Dispatcher and Renderer. See BakedOpDispatcher for how these baked drawing operations are - * resolved into Glops and rendered via BakedOpRenderer. - * - * This class is also the authoritative source for traversing RenderNodes, both for standard op - * traversal within a DisplayList, and for out of order RenderNode traversal for Z and projection. - */ -class FrameBuilder : public CanvasStateClient { -public: - struct LightGeometry { - Vector3 center; - float radius; - }; - - FrameBuilder(const SkRect& clip, uint32_t viewportWidth, uint32_t viewportHeight, - const LightGeometry& lightGeometry, Caches& caches); - - FrameBuilder(const LayerUpdateQueue& layerUpdateQueue, const LightGeometry& lightGeometry, - Caches& caches); - - void deferLayers(const LayerUpdateQueue& layers); - - void deferRenderNode(RenderNode& renderNode); - - void deferRenderNode(float tx, float ty, Rect clipRect, RenderNode& renderNode); - - void deferRenderNodeScene(const std::vector<sp<RenderNode> >& nodes, - const Rect& contentDrawBounds); - - virtual ~FrameBuilder() {} - - /** - * replayBakedOps() is templated based on what class will receive ops being replayed. - * - * It constructs a lookup array of lambdas, which allows a recorded BakeOpState to use - * state->op->opId to lookup a receiver that will be called when the op is replayed. - */ - template <typename StaticDispatcher, typename Renderer> - void replayBakedOps(Renderer& renderer) { - std::vector<OffscreenBuffer*> temporaryLayers; - finishDefer(); -/** - * Defines a LUT of lambdas which allow a recorded BakedOpState to use state->op->opId to - * dispatch the op via a method on a static dispatcher when the op is replayed. - * - * For example a BitmapOp would resolve, via the lambda lookup, to calling: - * - * StaticDispatcher::onBitmapOp(Renderer& renderer, const BitmapOp& op, const BakedOpState& state); - */ -#define X(Type) \ - [](void* renderer, const BakedOpState& state) { \ - StaticDispatcher::on##Type(*(static_cast<Renderer*>(renderer)), \ - static_cast<const Type&>(*(state.op)), state); \ - }, - static BakedOpReceiver unmergedReceivers[] = BUILD_RENDERABLE_OP_LUT(X); -#undef X - -/** - * Defines a LUT of lambdas which allow merged arrays of BakedOpState* to be passed to a - * static dispatcher when the group of merged ops is replayed. - */ -#define X(Type) \ - [](void* renderer, const MergedBakedOpList& opList) { \ - StaticDispatcher::onMerged##Type##s(*(static_cast<Renderer*>(renderer)), opList); \ - }, - static MergedOpReceiver mergedReceivers[] = BUILD_MERGEABLE_OP_LUT(X); -#undef X - - // Relay through layers in reverse order, since layers - // later in the list will be drawn by earlier ones - for (int i = mLayerBuilders.size() - 1; i >= 1; i--) { - GL_CHECKPOINT(MODERATE); - LayerBuilder& layer = *(mLayerBuilders[i]); - if (layer.renderNode) { - // cached HW layer - can't skip layer if empty - renderer.startRepaintLayer(layer.offscreenBuffer, layer.repaintRect); - GL_CHECKPOINT(MODERATE); - layer.replayBakedOpsImpl((void*)&renderer, unmergedReceivers, mergedReceivers); - GL_CHECKPOINT(MODERATE); - renderer.endLayer(); - } else if (!layer.empty()) { - // save layer - skip entire layer if empty (in which case, LayerOp has null layer). - layer.offscreenBuffer = renderer.startTemporaryLayer(layer.width, layer.height); - temporaryLayers.push_back(layer.offscreenBuffer); - GL_CHECKPOINT(MODERATE); - layer.replayBakedOpsImpl((void*)&renderer, unmergedReceivers, mergedReceivers); - GL_CHECKPOINT(MODERATE); - renderer.endLayer(); - } - } - - GL_CHECKPOINT(MODERATE); - if (CC_LIKELY(mDrawFbo0)) { - const LayerBuilder& fbo0 = *(mLayerBuilders[0]); - renderer.startFrame(fbo0.width, fbo0.height, fbo0.repaintRect); - GL_CHECKPOINT(MODERATE); - fbo0.replayBakedOpsImpl((void*)&renderer, unmergedReceivers, mergedReceivers); - GL_CHECKPOINT(MODERATE); - renderer.endFrame(fbo0.repaintRect); - } - - for (auto& temporaryLayer : temporaryLayers) { - renderer.recycleTemporaryLayer(temporaryLayer); - } - } - - void dump() const { - for (auto&& layer : mLayerBuilders) { - layer->dump(); - } - } - - /////////////////////////////////////////////////////////////////// - /// CanvasStateClient interface - /////////////////////////////////////////////////////////////////// - virtual void onViewportInitialized() override; - virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) override; - virtual GLuint getTargetFbo() const override { return 0; } - -private: - void finishDefer(); - enum class ChildrenSelectMode { Negative, Positive }; - void saveForLayer(uint32_t layerWidth, uint32_t layerHeight, float contentTranslateX, - float contentTranslateY, const Rect& repaintRect, const Vector3& lightCenter, - const BeginLayerOp* beginLayerOp, RenderNode* renderNode); - void restoreForLayer(); - - LayerBuilder& currentLayer() { return *(mLayerBuilders[mLayerStack.back()]); } - - BakedOpState* tryBakeOpState(const RecordedOp& recordedOp) { - return BakedOpState::tryConstruct(mAllocator, *mCanvasState.writableSnapshot(), recordedOp); - } - BakedOpState* tryBakeUnboundedOpState(const RecordedOp& recordedOp) { - return BakedOpState::tryConstructUnbounded(mAllocator, *mCanvasState.writableSnapshot(), - recordedOp); - } - - // should always be surrounded by a save/restore pair, and not called if DisplayList is null - void deferNodePropsAndOps(RenderNode& node); - - template <typename V> - void defer3dChildren(const ClipBase* reorderClip, ChildrenSelectMode mode, - const V& zTranslatedNodes); - - void deferShadow(const ClipBase* reorderClip, const RenderNodeOp& casterOp); - - void deferProjectedChildren(const RenderNode& renderNode); - - void deferNodeOps(const RenderNode& renderNode); - - void deferRenderNodeOpImpl(const RenderNodeOp& op); - - void replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers); - - SkPath* createFrameAllocatedPath() { return mAllocator.create<SkPath>(); } - - BakedOpState* deferStrokeableOp(const RecordedOp& op, batchid_t batchId, - BakedOpState::StrokeBehavior strokeBehavior = - BakedOpState::StrokeBehavior::StyleDefined, - bool expandForPathTexture = false); - -/** - * Declares all FrameBuilder::deferXXXXOp() methods for every RecordedOp type. - * - * These private methods are called from within deferImpl to defer each individual op - * type differently. - */ -#define X(Type) void defer##Type(const Type& op); - MAP_DEFERRABLE_OPS(X) -#undef X - - // contains single-frame objects, such as BakedOpStates, LayerBuilders, Batches - LinearAllocator mAllocator; - LinearStdAllocator<void*> mStdAllocator; - - // List of every deferred layer's render state. Replayed in reverse order to render a frame. - LsaVector<LayerBuilder*> mLayerBuilders; - - /* - * Stack of indices within mLayerBuilders representing currently active layers. If drawing - * layerA within a layerB, will contain, in order: - * - 0 (representing FBO 0, always present) - * - layerB's index - * - layerA's index - * - * Note that this doesn't vector doesn't always map onto all values of mLayerBuilders. When a - * layer is finished deferring, it will still be represented in mLayerBuilders, but it's index - * won't be in mLayerStack. This is because it can be replayed, but can't have any more drawing - * ops added to it. - */ - LsaVector<size_t> mLayerStack; - - CanvasState mCanvasState; - - Caches& mCaches; - - float mLightRadius; - - const bool mDrawFbo0; -}; - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/Lighting.h b/libs/hwui/Lighting.h new file mode 100644 index 000000000000..d972c2181aea --- /dev/null +++ b/libs/hwui/Lighting.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "Vector.h" + +namespace android { +namespace uirenderer { + +struct LightGeometry { + Vector3 center; + float radius; +}; + +struct LightInfo { + LightInfo() : LightInfo(0, 0) {} + LightInfo(uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) + : ambientShadowAlpha(ambientShadowAlpha), spotShadowAlpha(spotShadowAlpha) {} + uint8_t ambientShadowAlpha; + uint8_t spotShadowAlpha; +}; + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp deleted file mode 100644 index e1df1e7725b5..000000000000 --- a/libs/hwui/RecordingCanvas.cpp +++ /dev/null @@ -1,631 +0,0 @@ -/* - * 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 "RecordingCanvas.h" - -#include "DeferredLayerUpdater.h" -#include "RecordedOp.h" -#include "RenderNode.h" -#include "VectorDrawable.h" -#include "hwui/MinikinUtils.h" - -namespace android { -namespace uirenderer { - -RecordingCanvas::RecordingCanvas(size_t width, size_t height) - : mState(*this), mResourceCache(ResourceCache::getInstance()) { - resetRecording(width, height); -} - -RecordingCanvas::~RecordingCanvas() { - LOG_ALWAYS_FATAL_IF(mDisplayList, "Destroyed a RecordingCanvas during a record!"); -} - -void RecordingCanvas::resetRecording(int width, int height, RenderNode* node) { - LOG_ALWAYS_FATAL_IF(mDisplayList, "prepareDirty called a second time during a recording!"); - mDisplayList = new DisplayList(); - - mState.initializeRecordingSaveStack(width, height); - - mDeferredBarrierType = DeferredBarrierType::InOrder; -} - -DisplayList* RecordingCanvas::finishRecording() { - restoreToCount(1); - mPaintMap.clear(); - mRegionMap.clear(); - mPathMap.clear(); - DisplayList* displayList = mDisplayList; - mDisplayList = nullptr; - mSkiaCanvasProxy.reset(nullptr); - return displayList; -} - -void RecordingCanvas::insertReorderBarrier(bool enableReorder) { - if (enableReorder) { - mDeferredBarrierType = DeferredBarrierType::OutOfOrder; - mDeferredBarrierClip = getRecordedClip(); - } else { - mDeferredBarrierType = DeferredBarrierType::InOrder; - mDeferredBarrierClip = nullptr; - } -} - -SkCanvas* RecordingCanvas::asSkCanvas() { - LOG_ALWAYS_FATAL_IF(!mDisplayList, "attempting to get an SkCanvas when we are not recording!"); - if (!mSkiaCanvasProxy) { - mSkiaCanvasProxy.reset(new SkiaCanvasProxy(this)); - } - - // SkCanvas instances default to identity transform, but should inherit - // the state of this Canvas; if this code was in the SkiaCanvasProxy - // constructor, we couldn't cache mSkiaCanvasProxy. - SkMatrix parentTransform; - getMatrix(&parentTransform); - mSkiaCanvasProxy.get()->setMatrix(parentTransform); - - return mSkiaCanvasProxy.get(); -} - -// ---------------------------------------------------------------------------- -// CanvasStateClient implementation -// ---------------------------------------------------------------------------- - -void RecordingCanvas::onViewportInitialized() {} - -void RecordingCanvas::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) { - if (removed.flags & Snapshot::kFlagIsFboLayer) { - addOp(alloc().create_trivial<EndLayerOp>()); - } else if (removed.flags & Snapshot::kFlagIsLayer) { - addOp(alloc().create_trivial<EndUnclippedLayerOp>()); - } -} - -// ---------------------------------------------------------------------------- -// android/graphics/Canvas state operations -// ---------------------------------------------------------------------------- -// Save (layer) -int RecordingCanvas::save(SaveFlags::Flags flags) { - return mState.save((int)flags); -} - -void RecordingCanvas::RecordingCanvas::restore() { - mState.restore(); -} - -void RecordingCanvas::restoreToCount(int saveCount) { - mState.restoreToCount(saveCount); -} - -int RecordingCanvas::saveLayer(float left, float top, float right, float bottom, - const SkPaint* paint, SaveFlags::Flags flags) { - // force matrix/clip isolation for layer - flags |= SaveFlags::MatrixClip; - bool clippedLayer = flags & SaveFlags::ClipToLayer; - - const Snapshot& previous = *mState.currentSnapshot(); - - // initialize the snapshot as though it almost represents an FBO layer so deferred draw - // operations will be able to store and restore the current clip and transform info, and - // quick rejection will be correct (for display lists) - - Rect unmappedBounds(left, top, right, bottom); - unmappedBounds.roundOut(); - - // determine clipped bounds relative to previous viewport. - Rect visibleBounds = unmappedBounds; - previous.transform->mapRect(visibleBounds); - - if (CC_UNLIKELY(!clippedLayer && previous.transform->rectToRect() && - visibleBounds.contains(previous.getRenderTargetClip()))) { - // unlikely case where an unclipped savelayer is recorded with a clip it can use, - // as none of its unaffected/unclipped area is visible - clippedLayer = true; - flags |= SaveFlags::ClipToLayer; - } - - visibleBounds.doIntersect(previous.getRenderTargetClip()); - visibleBounds.snapToPixelBoundaries(); - visibleBounds.doIntersect(Rect(previous.getViewportWidth(), previous.getViewportHeight())); - - // Map visible bounds back to layer space, and intersect with parameter bounds - Rect layerBounds = visibleBounds; - if (CC_LIKELY(!layerBounds.isEmpty())) { - // if non-empty, can safely map by the inverse transform - Matrix4 inverse; - inverse.loadInverse(*previous.transform); - inverse.mapRect(layerBounds); - layerBounds.doIntersect(unmappedBounds); - } - - int saveValue = mState.save((int)flags); - Snapshot& snapshot = *mState.writableSnapshot(); - - // layerBounds is in original bounds space, but clipped by current recording clip - if (!layerBounds.isEmpty() && !unmappedBounds.isEmpty()) { - if (CC_LIKELY(clippedLayer)) { - auto previousClip = getRecordedClip(); // capture before new snapshot clip has changed - if (addOp(alloc().create_trivial<BeginLayerOp>( - unmappedBounds, - *previous.transform, // transform to *draw* with - previousClip, // clip to *draw* with - refPaint(paint))) >= 0) { - snapshot.flags |= Snapshot::kFlagIsLayer | Snapshot::kFlagIsFboLayer; - snapshot.initializeViewport(unmappedBounds.getWidth(), unmappedBounds.getHeight()); - snapshot.transform->loadTranslate(-unmappedBounds.left, -unmappedBounds.top, 0.0f); - - Rect clip = layerBounds; - clip.translate(-unmappedBounds.left, -unmappedBounds.top); - snapshot.resetClip(clip.left, clip.top, clip.right, clip.bottom); - snapshot.roundRectClipState = nullptr; - return saveValue; - } - } else { - if (addOp(alloc().create_trivial<BeginUnclippedLayerOp>( - unmappedBounds, *mState.currentSnapshot()->transform, getRecordedClip(), - refPaint(paint))) >= 0) { - snapshot.flags |= Snapshot::kFlagIsLayer; - return saveValue; - } - } - } - - // Layer not needed, so skip recording it... - if (CC_LIKELY(clippedLayer)) { - // ... and set empty clip to reject inner content, if possible - snapshot.resetClip(0, 0, 0, 0); - } - return saveValue; -} - -// Matrix -void RecordingCanvas::rotate(float degrees) { - if (degrees == 0) return; - - mState.rotate(degrees); -} - -void RecordingCanvas::scale(float sx, float sy) { - if (sx == 1 && sy == 1) return; - - mState.scale(sx, sy); -} - -void RecordingCanvas::skew(float sx, float sy) { - mState.skew(sx, sy); -} - -void RecordingCanvas::translate(float dx, float dy) { - if (dx == 0 && dy == 0) return; - - mState.translate(dx, dy, 0); -} - -// Clip -bool RecordingCanvas::getClipBounds(SkRect* outRect) const { - *outRect = mState.getLocalClipBounds().toSkRect(); - return !(outRect->isEmpty()); -} -bool RecordingCanvas::quickRejectRect(float left, float top, float right, float bottom) const { - return mState.quickRejectConservative(left, top, right, bottom); -} -bool RecordingCanvas::quickRejectPath(const SkPath& path) const { - SkRect bounds = path.getBounds(); - return mState.quickRejectConservative(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom); -} -bool RecordingCanvas::clipRect(float left, float top, float right, float bottom, SkClipOp op) { - return mState.clipRect(left, top, right, bottom, op); -} -bool RecordingCanvas::clipPath(const SkPath* path, SkClipOp op) { - return mState.clipPath(path, op); -} - -// ---------------------------------------------------------------------------- -// android/graphics/Canvas draw operations -// ---------------------------------------------------------------------------- -void RecordingCanvas::drawColor(int color, SkBlendMode mode) { - addOp(alloc().create_trivial<ColorOp>(getRecordedClip(), color, mode)); -} - -void RecordingCanvas::drawPaint(const SkPaint& paint) { - SkRect bounds; - if (getClipBounds(&bounds)) { - drawRect(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, paint); - } -} - -static Rect calcBoundsOfPoints(const float* points, int floatCount) { - Rect unmappedBounds(points[0], points[1], points[0], points[1]); - for (int i = 2; i < floatCount; i += 2) { - unmappedBounds.expandToCover(points[i], points[i + 1]); - } - return unmappedBounds; -} - -// Geometry -void RecordingCanvas::drawPoints(const float* points, int floatCount, const SkPaint& paint) { - if (CC_UNLIKELY(floatCount < 2 || paint.nothingToDraw())) return; - floatCount &= ~0x1; // round down to nearest two - - addOp(alloc().create_trivial<PointsOp>( - calcBoundsOfPoints(points, floatCount), *mState.currentSnapshot()->transform, - getRecordedClip(), refPaint(&paint), refBuffer<float>(points, floatCount), floatCount)); -} - -void RecordingCanvas::drawLines(const float* points, int floatCount, const SkPaint& paint) { - if (CC_UNLIKELY(floatCount < 4 || paint.nothingToDraw())) return; - floatCount &= ~0x3; // round down to nearest four - - addOp(alloc().create_trivial<LinesOp>( - calcBoundsOfPoints(points, floatCount), *mState.currentSnapshot()->transform, - getRecordedClip(), refPaint(&paint), refBuffer<float>(points, floatCount), floatCount)); -} - -void RecordingCanvas::drawRect(float left, float top, float right, float bottom, - const SkPaint& paint) { - if (CC_UNLIKELY(paint.nothingToDraw())) return; - - addOp(alloc().create_trivial<RectOp>(Rect(left, top, right, bottom), - *(mState.currentSnapshot()->transform), getRecordedClip(), - refPaint(&paint))); -} - -void RecordingCanvas::drawSimpleRects(const float* rects, int vertexCount, const SkPaint* paint) { - if (rects == nullptr) return; - - Vertex* rectData = (Vertex*)mDisplayList->allocator.create_trivial_array<Vertex>(vertexCount); - Vertex* vertex = rectData; - - float left = FLT_MAX; - float top = FLT_MAX; - float right = FLT_MIN; - float bottom = FLT_MIN; - for (int index = 0; index < vertexCount; index += 4) { - float l = rects[index + 0]; - float t = rects[index + 1]; - float r = rects[index + 2]; - float b = rects[index + 3]; - - Vertex::set(vertex++, l, t); - Vertex::set(vertex++, r, t); - Vertex::set(vertex++, l, b); - Vertex::set(vertex++, r, b); - - left = std::min(left, l); - top = std::min(top, t); - right = std::max(right, r); - bottom = std::max(bottom, b); - } - addOp(alloc().create_trivial<SimpleRectsOp>( - Rect(left, top, right, bottom), *(mState.currentSnapshot()->transform), - getRecordedClip(), refPaint(paint), rectData, vertexCount)); -} - -void RecordingCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) { - if (CC_UNLIKELY(paint.nothingToDraw())) return; - - if (paint.getStyle() == SkPaint::kFill_Style && - (!paint.isAntiAlias() || mState.currentTransform()->isSimple())) { - int count = 0; - Vector<float> rects; - SkRegion::Iterator it(region); - while (!it.done()) { - const SkIRect& r = it.rect(); - rects.push(r.fLeft); - rects.push(r.fTop); - rects.push(r.fRight); - rects.push(r.fBottom); - count += 4; - it.next(); - } - drawSimpleRects(rects.array(), count, &paint); - } else { - SkRegion::Iterator it(region); - while (!it.done()) { - const SkIRect& r = it.rect(); - drawRect(r.fLeft, r.fTop, r.fRight, r.fBottom, paint); - it.next(); - } - } -} - -void RecordingCanvas::drawRoundRect(float left, float top, float right, float bottom, float rx, - float ry, const SkPaint& paint) { - if (CC_UNLIKELY(paint.nothingToDraw())) return; - - if (CC_LIKELY(MathUtils::isPositive(rx) || MathUtils::isPositive(ry))) { - addOp(alloc().create_trivial<RoundRectOp>(Rect(left, top, right, bottom), - *(mState.currentSnapshot()->transform), - getRecordedClip(), refPaint(&paint), rx, ry)); - } else { - drawRect(left, top, right, bottom, paint); - } -} - -void RecordingCanvas::drawRoundRect(CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top, - CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom, - CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry, - CanvasPropertyPaint* paint) { - mDisplayList->ref(left); - mDisplayList->ref(top); - mDisplayList->ref(right); - mDisplayList->ref(bottom); - mDisplayList->ref(rx); - mDisplayList->ref(ry); - mDisplayList->ref(paint); - refBitmapsInShader(paint->value.getShader()); - addOp(alloc().create_trivial<RoundRectPropsOp>( - *(mState.currentSnapshot()->transform), getRecordedClip(), &paint->value, &left->value, - &top->value, &right->value, &bottom->value, &rx->value, &ry->value)); -} - -void RecordingCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) { - // TODO: move to Canvas.h - if (CC_UNLIKELY(radius <= 0 || paint.nothingToDraw())) return; - - drawOval(x - radius, y - radius, x + radius, y + radius, paint); -} - -void RecordingCanvas::drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y, - CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) { - mDisplayList->ref(x); - mDisplayList->ref(y); - mDisplayList->ref(radius); - mDisplayList->ref(paint); - refBitmapsInShader(paint->value.getShader()); - addOp(alloc().create_trivial<CirclePropsOp>(*(mState.currentSnapshot()->transform), - getRecordedClip(), &paint->value, &x->value, - &y->value, &radius->value)); -} - -void RecordingCanvas::drawOval(float left, float top, float right, float bottom, - const SkPaint& paint) { - if (CC_UNLIKELY(paint.nothingToDraw())) return; - - addOp(alloc().create_trivial<OvalOp>(Rect(left, top, right, bottom), - *(mState.currentSnapshot()->transform), getRecordedClip(), - refPaint(&paint))); -} - -void RecordingCanvas::drawArc(float left, float top, float right, float bottom, float startAngle, - float sweepAngle, bool useCenter, const SkPaint& paint) { - if (CC_UNLIKELY(paint.nothingToDraw())) return; - - if (fabs(sweepAngle) >= 360.0f) { - drawOval(left, top, right, bottom, paint); - } else { - addOp(alloc().create_trivial<ArcOp>( - Rect(left, top, right, bottom), *(mState.currentSnapshot()->transform), - getRecordedClip(), refPaint(&paint), startAngle, sweepAngle, useCenter)); - } -} - -void RecordingCanvas::drawPath(const SkPath& path, const SkPaint& paint) { - if (CC_UNLIKELY(paint.nothingToDraw())) return; - - addOp(alloc().create_trivial<PathOp>(Rect(path.getBounds()), - *(mState.currentSnapshot()->transform), getRecordedClip(), - refPaint(&paint), refPath(&path))); -} - -void RecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) { - mDisplayList->ref(tree); - mDisplayList->vectorDrawables.push_back(tree); - addOp(alloc().create_trivial<VectorDrawableOp>( - tree, Rect(tree->stagingProperties()->getBounds()), - *(mState.currentSnapshot()->transform), getRecordedClip())); -} - -// Bitmap-based -void RecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) { - save(SaveFlags::Matrix); - translate(left, top); - drawBitmap(bitmap, paint); - restore(); -} - -void RecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) { - if (matrix.isIdentity()) { - drawBitmap(bitmap, paint); - } else if (!(matrix.getType() & ~(SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) && - MathUtils::isPositive(matrix.getScaleX()) && - MathUtils::isPositive(matrix.getScaleY())) { - // SkMatrix::isScaleTranslate() not available in L - SkRect src; - SkRect dst; - bitmap.getBounds(&src); - matrix.mapRect(&dst, src); - drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom, dst.fLeft, dst.fTop, - dst.fRight, dst.fBottom, paint); - } else { - save(SaveFlags::Matrix); - concat(matrix); - drawBitmap(bitmap, paint); - restore(); - } -} - -void RecordingCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight, - float srcBottom, float dstLeft, float dstTop, float dstRight, - float dstBottom, const SkPaint* paint) { - if (srcLeft == 0 && srcTop == 0 && srcRight == bitmap.width() && srcBottom == bitmap.height() && - (srcBottom - srcTop == dstBottom - dstTop) && (srcRight - srcLeft == dstRight - dstLeft)) { - // transform simple rect to rect drawing case into position bitmap ops, since they merge - save(SaveFlags::Matrix); - translate(dstLeft, dstTop); - drawBitmap(bitmap, paint); - restore(); - } else { - addOp(alloc().create_trivial<BitmapRectOp>( - Rect(dstLeft, dstTop, dstRight, dstBottom), *(mState.currentSnapshot()->transform), - getRecordedClip(), refPaint(paint), refBitmap(bitmap), - Rect(srcLeft, srcTop, srcRight, srcBottom))); - } -} - -void RecordingCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight, - const float* vertices, const int* colors, - const SkPaint* paint) { - int vertexCount = (meshWidth + 1) * (meshHeight + 1); - addOp(alloc().create_trivial<BitmapMeshOp>( - calcBoundsOfPoints(vertices, vertexCount * 2), *(mState.currentSnapshot()->transform), - getRecordedClip(), refPaint(paint), refBitmap(bitmap), meshWidth, meshHeight, - refBuffer<float>(vertices, vertexCount * 2), // 2 floats per vertex - refBuffer<int>(colors, vertexCount))); // 1 color per vertex -} - -void RecordingCanvas::drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& patch, - float dstLeft, float dstTop, float dstRight, float dstBottom, - const SkPaint* paint) { - addOp(alloc().create_trivial<PatchOp>(Rect(dstLeft, dstTop, dstRight, dstBottom), - *(mState.currentSnapshot()->transform), getRecordedClip(), - refPaint(paint), refBitmap(bitmap), refPatch(&patch))); -} - -double RecordingCanvas::drawAnimatedImage(AnimatedImageDrawable*) { - // Unimplemented - return 0; -} - -// Text -void RecordingCanvas::drawGlyphs(ReadGlyphFunc glyphFunc, int glyphCount, const SkPaint& paint, - float x, float y, float boundsLeft, float boundsTop, - float boundsRight, float boundsBottom, float totalAdvance) { - if (glyphCount <= 0 || paint.nothingToDraw()) return; - uint16_t* glyphs = (glyph_t*)alloc().alloc<glyph_t>(glyphCount * sizeof(glyph_t)); - float* positions = (float*)alloc().alloc<float>(2 * glyphCount * sizeof(float)); - glyphFunc(glyphs, positions); - - // TODO: either must account for text shadow in bounds, or record separate ops for text shadows - addOp(alloc().create_trivial<TextOp>(Rect(boundsLeft, boundsTop, boundsRight, boundsBottom), - *(mState.currentSnapshot()->transform), getRecordedClip(), - refPaint(&paint), glyphs, positions, glyphCount, x, y)); - drawTextDecorations(x, y, totalAdvance, paint); -} - -void RecordingCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset, - const SkPaint& paint, const SkPath& path, size_t start, - size_t end) { - uint16_t glyphs[1]; - for (size_t i = start; i < end; i++) { - glyphs[0] = layout.getGlyphId(i); - float x = hOffset + layout.getX(i); - float y = vOffset + layout.getY(i); - if (paint.nothingToDraw()) return; - const uint16_t* tempGlyphs = refBuffer<glyph_t>(glyphs, 1); - addOp(alloc().create_trivial<TextOnPathOp>(*(mState.currentSnapshot()->transform), - getRecordedClip(), refPaint(&paint), tempGlyphs, - 1, refPath(&path), x, y)); - } -} - -void RecordingCanvas::drawBitmap(Bitmap& bitmap, const SkPaint* paint) { - addOp(alloc().create_trivial<BitmapOp>(Rect(bitmap.width(), bitmap.height()), - *(mState.currentSnapshot()->transform), - getRecordedClip(), refPaint(paint), refBitmap(bitmap))); -} - -void RecordingCanvas::drawRenderNode(RenderNode* renderNode) { - auto&& stagingProps = renderNode->stagingProperties(); - RenderNodeOp* op = alloc().create_trivial<RenderNodeOp>( - Rect(stagingProps.getWidth(), stagingProps.getHeight()), - *(mState.currentSnapshot()->transform), getRecordedClip(), renderNode); - int opIndex = addOp(op); - if (CC_LIKELY(opIndex >= 0)) { - int childIndex = mDisplayList->addChild(op); - - // update the chunk's child indices - DisplayList::Chunk& chunk = mDisplayList->chunks.back(); - chunk.endChildIndex = childIndex + 1; - - if (renderNode->stagingProperties().isProjectionReceiver()) { - // use staging property, since recording on UI thread - mDisplayList->projectionReceiveIndex = opIndex; - } - } -} - -void RecordingCanvas::drawLayer(DeferredLayerUpdater* layerHandle) { - // We ref the DeferredLayerUpdater due to its thread-safe ref-counting semantics. - mDisplayList->ref(layerHandle); - - LOG_ALWAYS_FATAL_IF(layerHandle->getBackingLayerApi() != Layer::Api::OpenGL); - // Note that the backing layer has *not* yet been updated, so don't trust - // its width, height, transform, etc...! - addOp(alloc().create_trivial<TextureLayerOp>( - Rect(layerHandle->getWidth(), layerHandle->getHeight()), - *(mState.currentSnapshot()->transform), getRecordedClip(), layerHandle)); -} - -void RecordingCanvas::callDrawGLFunction(Functor* functor, GlFunctorLifecycleListener* listener) { - mDisplayList->functors.push_back({functor, listener}); - mDisplayList->ref(listener); - addOp(alloc().create_trivial<FunctorOp>(*(mState.currentSnapshot()->transform), - getRecordedClip(), functor)); -} - -int RecordingCanvas::addOp(RecordedOp* op) { - // skip op with empty clip - if (op->localClip && op->localClip->rect.isEmpty()) { - // NOTE: this rejection happens after op construction/content ref-ing, so content ref'd - // and held by renderthread isn't affected by clip rejection. - // Could rewind alloc here if desired, but callers would have to not touch op afterwards. - return -1; - } - - int insertIndex = mDisplayList->ops.size(); - mDisplayList->ops.push_back(op); - if (mDeferredBarrierType != DeferredBarrierType::None) { - // op is first in new chunk - mDisplayList->chunks.emplace_back(); - DisplayList::Chunk& newChunk = mDisplayList->chunks.back(); - newChunk.beginOpIndex = insertIndex; - newChunk.endOpIndex = insertIndex + 1; - newChunk.reorderChildren = (mDeferredBarrierType == DeferredBarrierType::OutOfOrder); - newChunk.reorderClip = mDeferredBarrierClip; - - int nextChildIndex = mDisplayList->children.size(); - newChunk.beginChildIndex = newChunk.endChildIndex = nextChildIndex; - mDeferredBarrierType = DeferredBarrierType::None; - } else { - // standard case - append to existing chunk - mDisplayList->chunks.back().endOpIndex = insertIndex + 1; - } - return insertIndex; -} - -void RecordingCanvas::refBitmapsInShader(const SkShader* shader) { - if (!shader) return; - - // If this paint has an SkShader that has an SkBitmap add - // it to the bitmap pile - SkBitmap bitmap; - SkShader::TileMode xy[2]; - if (shader->isABitmap(&bitmap, nullptr, xy)) { - Bitmap* hwuiBitmap = static_cast<Bitmap*>(bitmap.pixelRef()); - refBitmap(*hwuiBitmap); - return; - } - SkShader::ComposeRec rec; - if (shader->asACompose(&rec)) { - refBitmapsInShader(rec.fShaderA); - refBitmapsInShader(rec.fShaderB); - return; - } -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h deleted file mode 100644 index e663402a80f3..000000000000 --- a/libs/hwui/RecordingCanvas.h +++ /dev/null @@ -1,320 +0,0 @@ -/* - * 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_RECORDING_CANVAS_H -#define ANDROID_HWUI_RECORDING_CANVAS_H - -#include "CanvasState.h" -#include "DisplayList.h" -#include "ResourceCache.h" -#include "SkiaCanvasProxy.h" -#include "Snapshot.h" -#include "hwui/Bitmap.h" -#include "hwui/Canvas.h" -#include "utils/LinearAllocator.h" -#include "utils/Macros.h" - -#include <SkDrawFilter.h> -#include <SkPaint.h> -#include <SkTLazy.h> - -#include <vector> - -namespace android { -namespace uirenderer { - -struct ClipBase; -class DeferredLayerUpdater; -struct RecordedOp; - -class ANDROID_API RecordingCanvas : public Canvas, public CanvasStateClient { - enum class DeferredBarrierType { - None, - InOrder, - OutOfOrder, - }; - -public: - RecordingCanvas(size_t width, size_t height); - virtual ~RecordingCanvas(); - - virtual void resetRecording(int width, int height, RenderNode* node = nullptr) override; - virtual WARN_UNUSED_RESULT DisplayList* finishRecording() override; - // ---------------------------------------------------------------------------- - // MISC HWUI OPERATIONS - TODO: CATEGORIZE - // ---------------------------------------------------------------------------- - virtual void insertReorderBarrier(bool enableReorder) override; - - virtual void drawLayer(DeferredLayerUpdater* layerHandle) override; - virtual void drawRenderNode(RenderNode* renderNode) override; - virtual void callDrawGLFunction(Functor* functor, - GlFunctorLifecycleListener* listener) override; - - // ---------------------------------------------------------------------------- - // CanvasStateClient interface - // ---------------------------------------------------------------------------- - virtual void onViewportInitialized() override; - virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) override; - virtual GLuint getTargetFbo() const override { return -1; } - - // ---------------------------------------------------------------------------- - // HWUI Canvas draw operations - // ---------------------------------------------------------------------------- - - virtual void drawRoundRect(CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top, - CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom, - CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry, - CanvasPropertyPaint* paint) override; - virtual void drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y, - CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) override; - - // ---------------------------------------------------------------------------- - // android/graphics/Canvas interface - // ---------------------------------------------------------------------------- - virtual SkCanvas* asSkCanvas() override; - - virtual void setBitmap(const SkBitmap& bitmap) override { - LOG_ALWAYS_FATAL("RecordingCanvas is not backed by a bitmap."); - } - - virtual bool isOpaque() override { return false; } - virtual int width() override { return mState.getWidth(); } - virtual int height() override { return mState.getHeight(); } - - // ---------------------------------------------------------------------------- - // android/graphics/Canvas state operations - // ---------------------------------------------------------------------------- - // Save (layer) - virtual int getSaveCount() const override { return mState.getSaveCount(); } - virtual int save(SaveFlags::Flags flags) override; - virtual void restore() override; - virtual void restoreToCount(int saveCount) override; - - virtual int saveLayer(float left, float top, float right, float bottom, const SkPaint* paint, - SaveFlags::Flags flags) override; - virtual int saveLayerAlpha(float left, float top, float right, float bottom, int alpha, - SaveFlags::Flags flags) override { - SkPaint paint; - paint.setAlpha(alpha); - return saveLayer(left, top, right, bottom, &paint, flags); - } - - // Matrix - virtual void getMatrix(SkMatrix* outMatrix) const override { mState.getMatrix(outMatrix); } - virtual void setMatrix(const SkMatrix& matrix) override { mState.setMatrix(matrix); } - - virtual void concat(const SkMatrix& matrix) override { mState.concatMatrix(matrix); } - virtual void rotate(float degrees) override; - virtual void scale(float sx, float sy) override; - virtual void skew(float sx, float sy) override; - virtual void translate(float dx, float dy) override; - - // Clip - virtual bool getClipBounds(SkRect* outRect) const override; - virtual bool quickRejectRect(float left, float top, float right, float bottom) const override; - virtual bool quickRejectPath(const SkPath& path) const override; - - virtual bool clipRect(float left, float top, float right, float bottom, SkClipOp op) override; - virtual bool clipPath(const SkPath* path, SkClipOp op) override; - - // Misc - virtual SkDrawFilter* getDrawFilter() override { return mDrawFilter.get(); } - virtual void setDrawFilter(SkDrawFilter* filter) override { - mDrawFilter.reset(SkSafeRef(filter)); - } - - // ---------------------------------------------------------------------------- - // android/graphics/Canvas draw operations - // ---------------------------------------------------------------------------- - virtual void drawColor(int color, SkBlendMode mode) override; - virtual void drawPaint(const SkPaint& paint) override; - - // Geometry - virtual void drawPoint(float x, float y, const SkPaint& paint) override { - float points[2] = {x, y}; - drawPoints(points, 2, paint); - } - virtual void drawPoints(const float* points, int floatCount, const SkPaint& paint) override; - virtual void drawLine(float startX, float startY, float stopX, float stopY, - const SkPaint& paint) override { - float points[4] = {startX, startY, stopX, stopY}; - drawLines(points, 4, paint); - } - virtual void drawLines(const float* points, int floatCount, const SkPaint& paint) override; - virtual void drawRect(float left, float top, float right, float bottom, - const SkPaint& paint) override; - virtual void drawRegion(const SkRegion& region, const SkPaint& paint) override; - virtual void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, - const SkPaint& paint) override; - virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) override; - virtual void drawOval(float left, float top, float right, float bottom, - const SkPaint& paint) override; - virtual void drawArc(float left, float top, float right, float bottom, float startAngle, - float sweepAngle, bool useCenter, const SkPaint& paint) override; - virtual void drawPath(const SkPath& path, const SkPaint& paint) override; - virtual void drawVertices(const SkVertices*, SkBlendMode, const SkPaint& paint) - override { /* RecordingCanvas does not support drawVertices(); ignore */ - } - - virtual void drawVectorDrawable(VectorDrawableRoot* tree) override; - - // Bitmap-based - virtual void drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) override; - virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) override; - virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight, - float srcBottom, float dstLeft, float dstTop, float dstRight, - float dstBottom, const SkPaint* paint) override; - virtual void drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight, - const float* vertices, const int* colors, - const SkPaint* paint) override; - virtual void drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& chunk, float dstLeft, - float dstTop, float dstRight, float dstBottom, - const SkPaint* paint) override; - virtual double drawAnimatedImage(AnimatedImageDrawable*) override; - - // Text - virtual bool drawTextAbsolutePos() const override { return false; } - -protected: - virtual void drawGlyphs(ReadGlyphFunc glyphFunc, int count, const SkPaint& paint, float x, - float y, float boundsLeft, float boundsTop, float boundsRight, - float boundsBottom, float totalAdvance) override; - virtual void drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset, - const SkPaint& paint, const SkPath& path, size_t start, - size_t end) override; - -private: - const ClipBase* getRecordedClip() { - return mState.writableSnapshot()->mutateClipArea().serializeClip(alloc()); - } - - void drawBitmap(Bitmap& bitmap, const SkPaint* paint); - void drawSimpleRects(const float* rects, int vertexCount, const SkPaint* paint); - - int addOp(RecordedOp* op); - // ---------------------------------------------------------------------------- - // lazy object copy - // ---------------------------------------------------------------------------- - LinearAllocator& alloc() { return mDisplayList->allocator; } - - void refBitmapsInShader(const SkShader* shader); - - template <class T> - inline const T* refBuffer(const T* srcBuffer, int32_t count) { - if (!srcBuffer) return nullptr; - - T* dstBuffer = (T*)mDisplayList->allocator.alloc<T>(count * sizeof(T)); - memcpy(dstBuffer, srcBuffer, count * sizeof(T)); - return dstBuffer; - } - - inline const SkPath* refPath(const SkPath* path) { - if (!path) return nullptr; - - // The points/verbs within the path are refcounted so this copy operation - // is inexpensive and maintains the generationID of the original path. - const SkPath* cachedPath = new SkPath(*path); - mDisplayList->pathResources.push_back(cachedPath); - return cachedPath; - } - - /** - * Returns a RenderThread-safe, const copy of the SkPaint parameter passed in - * (with deduping based on paint hash / equality check) - */ - inline const SkPaint* refPaint(const SkPaint* paint) { - if (!paint) return nullptr; - - // If there is a draw filter apply it here and store the modified paint - // so that we don't need to modify the paint every time we access it. - SkTLazy<SkPaint> filteredPaint; - if (mDrawFilter.get()) { - filteredPaint.set(*paint); - mDrawFilter->filter(filteredPaint.get(), SkDrawFilter::kPaint_Type); - paint = filteredPaint.get(); - } - - // compute the hash key for the paint and check the cache. - const uint32_t key = paint->getHash(); - const SkPaint* cachedPaint = mPaintMap.valueFor(key); - // In the unlikely event that 2 unique paints have the same hash we do a - // object equality check to ensure we don't erroneously dedup them. - if (cachedPaint == nullptr || *cachedPaint != *paint) { - cachedPaint = new SkPaint(*paint); - mDisplayList->paints.emplace_back(cachedPaint); - // replaceValueFor() performs an add if the entry doesn't exist - mPaintMap.replaceValueFor(key, cachedPaint); - refBitmapsInShader(cachedPaint->getShader()); - } - - return cachedPaint; - } - - inline const SkRegion* refRegion(const SkRegion* region) { - if (!region) { - return region; - } - - const SkRegion* cachedRegion = mRegionMap.valueFor(region); - // TODO: Add generation ID to SkRegion - if (cachedRegion == nullptr) { - std::unique_ptr<const SkRegion> copy(new SkRegion(*region)); - cachedRegion = copy.get(); - mDisplayList->regions.push_back(std::move(copy)); - - // replaceValueFor() performs an add if the entry doesn't exist - mRegionMap.replaceValueFor(region, cachedRegion); - } - - return cachedRegion; - } - - inline Bitmap* refBitmap(Bitmap& bitmap) { - // Note that this assumes the bitmap is immutable. There are cases this won't handle - // correctly, such as creating the bitmap from scratch, drawing with it, changing its - // contents, and drawing again. The only fix would be to always copy it the first time, - // which doesn't seem worth the extra cycles for this unlikely case. - - // this is required because sk_sp's ctor adopts the pointer, - // but does not increment the refcount, - bitmap.ref(); - mDisplayList->bitmapResources.emplace_back(&bitmap); - return &bitmap; - } - - inline const Res_png_9patch* refPatch(const Res_png_9patch* patch) { - mDisplayList->patchResources.push_back(patch); - mResourceCache.incrementRefcount(patch); - return patch; - } - - DefaultKeyedVector<uint32_t, const SkPaint*> mPaintMap; - DefaultKeyedVector<const SkPath*, const SkPath*> mPathMap; - DefaultKeyedVector<const SkRegion*, const SkRegion*> mRegionMap; - - CanvasState mState; - std::unique_ptr<SkiaCanvasProxy> mSkiaCanvasProxy; - ResourceCache& mResourceCache; - DeferredBarrierType mDeferredBarrierType = DeferredBarrierType::None; - const ClipBase* mDeferredBarrierClip = nullptr; - DisplayList* mDisplayList = nullptr; - sk_sp<SkDrawFilter> mDrawFilter; -}; // class RecordingCanvas - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_RECORDING_CANVAS_H diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 62b80c43ebb7..e9039001bd53 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -381,78 +381,6 @@ void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) } } -/** - * Organizes the DisplayList hierarchy to prepare for background projection reordering. - * - * This should be called before a call to defer() or drawDisplayList() - * - * Each DisplayList that serves as a 3d root builds its list of composited children, - * which are flagged to not draw in the standard draw loop. - */ -void RenderNode::computeOrdering() { - ATRACE_CALL(); - mProjectedNodes.clear(); - - // TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that - // transform properties are applied correctly to top level children - if (mDisplayList == nullptr) return; - for (unsigned int i = 0; i < mDisplayList->getChildren().size(); i++) { - RenderNodeOp* childOp = mDisplayList->getChildren()[i]; - childOp->renderNode->computeOrderingImpl(childOp, &mProjectedNodes, &mat4::identity()); - } -} - -void RenderNode::computeOrderingImpl( - RenderNodeOp* opState, std::vector<RenderNodeOp*>* compositedChildrenOfProjectionSurface, - const mat4* transformFromProjectionSurface) { - 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->localMatrix); - - if (properties().getProjectBackwards()) { - // composited projectee, flag for out of order draw, save matrix, and store in proj surface - opState->skipInOrderDraw = true; - opState->transformFromCompositingAncestor = localTransformFromProjectionSurface; - compositedChildrenOfProjectionSurface->push_back(opState); - } else { - // standard in order draw - 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++) { - RenderNodeOp* childOp = mDisplayList->getChildren()[i]; - RenderNode* child = childOp->renderNode; - - std::vector<RenderNodeOp*>* projectionChildren = nullptr; - const mat4* projectionTransform = nullptr; - if (isProjectionReceiver && !child->properties().getProjectBackwards()) { - // if receiving projections, collect projecting descendant - - // Note that if a direct descendant is projecting backwards, we pass its - // grandparent projection collection, since it shouldn't project onto its - // parent, where it will already be drawing. - projectionChildren = &mProjectedNodes; - projectionTransform = &mat4::identity(); - } else { - if (!haveAppliedPropertiesToProjection) { - applyViewPropertyTransforms(localTransformFromProjectionSurface); - haveAppliedPropertiesToProjection = true; - } - projectionChildren = compositedChildrenOfProjectionSurface; - projectionTransform = &localTransformFromProjectionSurface; - } - child->computeOrderingImpl(childOp, projectionChildren, projectionTransform); - } - } -} - const SkPath* RenderNode::getClippedOutline(const SkRect& clipRect) const { const SkPath* outlinePath = properties().getOutline().getPath(); const uint32_t outlineID = outlinePath->getGenerationID(); diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index 1e0d4e2c2254..45a53f9a37df 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -48,8 +48,6 @@ namespace android { namespace uirenderer { class CanvasState; -class DisplayListOp; -class FrameBuilder; class OffscreenBuffer; class Rect; class SkiaShader; @@ -76,7 +74,6 @@ class RenderNode; */ class RenderNode : public VirtualLightRefBase { friend class TestUtils; // allow TestUtils to access syncDisplayList / syncProperties - friend class FrameBuilder; public: enum DirtyPropertyMask { @@ -104,8 +101,6 @@ public: ANDROID_API void setStagingDisplayList(DisplayList* newData); - void computeOrdering(); - ANDROID_API void output(); ANDROID_API int getDebugSize(); diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h index da52a9503377..e84b9acffca7 100644 --- a/libs/hwui/VectorDrawable.h +++ b/libs/hwui/VectorDrawable.h @@ -718,6 +718,8 @@ private: } // namespace VectorDrawable typedef VectorDrawable::Path::Data PathData; +typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot; + } // namespace uirenderer } // namespace android diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp index b453227ba770..30f6f12b424e 100644 --- a/libs/hwui/hwui/Canvas.cpp +++ b/libs/hwui/hwui/Canvas.cpp @@ -19,7 +19,6 @@ #include "MinikinUtils.h" #include "Paint.h" #include "Properties.h" -#include "RecordingCanvas.h" #include "RenderNode.h" #include "Typeface.h" #include "pipeline/skia/SkiaRecordingCanvas.h" diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index f341cf96120d..341b0c56a50c 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -44,8 +44,16 @@ namespace uirenderer { class CanvasPropertyPaint; class CanvasPropertyPrimitive; class DeferredLayerUpdater; -class DisplayList; class RenderNode; + +namespace skiapipeline { +class SkiaDisplayList; +} + +/** + * Data structure that holds the list of commands used in display list stream + */ +using DisplayList = skiapipeline::SkiaDisplayList; } namespace SaveFlags { diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.h b/libs/hwui/pipeline/skia/GLFunctorDrawable.h index af57d7d33c2c..d9e65c95444e 100644 --- a/libs/hwui/pipeline/skia/GLFunctorDrawable.h +++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.h @@ -16,6 +16,8 @@ #pragma once +#include "GlFunctorLifecycleListener.h" + #include <SkCanvas.h> #include <SkDrawable.h> @@ -25,8 +27,6 @@ namespace android { namespace uirenderer { -class GlFunctorLifecycleListener; - namespace skiapipeline { /** diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp index 6c04d7862979..1b816febf846 100644 --- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp +++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp @@ -25,6 +25,19 @@ namespace android { namespace uirenderer { namespace skiapipeline { +RenderNodeDrawable::RenderNodeDrawable(RenderNode* node, SkCanvas* canvas, bool composeLayer, + bool inReorderingSection) + : mRenderNode(node) + , mRecordedTransform(canvas->getTotalMatrix()) + , mComposeLayer(composeLayer) + , mInReorderingSection(inReorderingSection) {} + +RenderNodeDrawable::~RenderNodeDrawable() { + // Just here to move the destructor into the cpp file where we can access RenderNode. + + // TODO: Detangle the header nightmare. +} + void RenderNodeDrawable::drawBackwardsProjectedNodes(SkCanvas* canvas, const SkiaDisplayList& displayList, int nestLevel) { @@ -115,7 +128,6 @@ void RenderNodeDrawable::forceDraw(SkCanvas* canvas) { return; } - SkASSERT(renderNode->getDisplayList()->isSkiaDL()); SkiaDisplayList* displayList = (SkiaDisplayList*)renderNode->getDisplayList(); SkAutoCanvasRestore acr(canvas, true); diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.h b/libs/hwui/pipeline/skia/RenderNodeDrawable.h index ef21cd8a29b5..6594bd22c0b1 100644 --- a/libs/hwui/pipeline/skia/RenderNodeDrawable.h +++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.h @@ -47,11 +47,9 @@ public: * layer into the canvas. */ explicit RenderNodeDrawable(RenderNode* node, SkCanvas* canvas, bool composeLayer = true, - bool inReorderingSection = false) - : mRenderNode(node) - , mRecordedTransform(canvas->getTotalMatrix()) - , mComposeLayer(composeLayer) - , mInReorderingSection(inReorderingSection) {} + bool inReorderingSection = false); + + ~RenderNodeDrawable(); /** * Draws into the canvas this render node and its children. If the node is marked as a diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h index 818ec114a5b3..58b9242e087f 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.h +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h @@ -16,10 +16,11 @@ #pragma once -#include "DisplayList.h" #include "hwui/AnimatedImageDrawable.h" #include "GLFunctorDrawable.h" #include "RenderNodeDrawable.h" +#include "TreeInfo.h" +#include "utils/LinearAllocator.h" #include <SkLiteDL.h> #include <SkLiteRecorder.h> @@ -28,8 +29,17 @@ namespace android { namespace uirenderer { +namespace renderthread { +class CanvasContext; +} + class Outline; +namespace VectorDrawable { +class Tree; +}; +typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot; + namespace skiapipeline { /** @@ -38,10 +48,14 @@ namespace skiapipeline { * runtime. The downside of this inheritance is that we pay for the overhead * of the parent class construction/destruction without any real benefit. */ -class SkiaDisplayList : public DisplayList { +class SkiaDisplayList { public: - SkiaDisplayList() { SkASSERT(projectionReceiveIndex == -1); } - virtual ~SkiaDisplayList() { + // index of DisplayListOp restore, after which projected descendants should be drawn + int projectionReceiveIndex = -1; + + size_t getUsedSize() { return allocator.usedSize(); } + + ~SkiaDisplayList() { /* Given that we are using a LinearStdAllocator to store some of the * SkDrawable contents we must ensure that any other object that is * holding a reference to those drawables is destroyed prior to their @@ -68,29 +82,27 @@ public: return allocator.create<T>(std::forward<Params>(params)...); } - bool isSkiaDL() const override { return true; } - /** * Returns true if the DisplayList does not have any recorded content */ - bool isEmpty() const override { return mDisplayList.empty(); } + bool isEmpty() const { return mDisplayList.empty(); } /** * Returns true if this list directly contains a GLFunctor drawing command. */ - bool hasFunctor() const override { return !mChildFunctors.empty(); } + bool hasFunctor() const { return !mChildFunctors.empty(); } /** * Returns true if this list directly contains a VectorDrawable drawing command. */ - bool hasVectorDrawables() const override { return !mVectorDrawables.empty(); } + bool hasVectorDrawables() const { return !mVectorDrawables.empty(); } /** * Attempts to reset and reuse this DisplayList. * * @return true if the displayList will be reused and therefore should not be deleted */ - bool reuseDisplayList(RenderNode* node, renderthread::CanvasContext* context) override; + bool reuseDisplayList(RenderNode* node, renderthread::CanvasContext* context); /** * ONLY to be called by RenderNode::syncDisplayList so that we can notify any @@ -99,7 +111,7 @@ public: * NOTE: This function can be folded into RenderNode when we no longer need * to subclass from DisplayList */ - void syncContents() override; + void syncContents(); /** * ONLY to be called by RenderNode::prepareTree in order to prepare this @@ -116,12 +128,12 @@ public: bool prepareListAndChildren( TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer, - std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn) override; + std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn); /** * Calls the provided function once for each child of this DisplayList */ - void updateChildren(std::function<void(RenderNode*)> updateFn) override; + void updateChildren(std::function<void(RenderNode*)> updateFn); /** * Returns true if there is a child render node that is a projection receiver. @@ -134,7 +146,9 @@ public: void draw(SkCanvas* canvas) { mDisplayList.draw(canvas); } - void output(std::ostream& output, uint32_t level) override; + void output(std::ostream& output, uint32_t level); + + LinearAllocator allocator; /** * We use std::deque here because (1) we need to iterate through these diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index cfcfd2b74a5a..15277f175350 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -25,6 +25,7 @@ #include "renderstate/RenderState.h" #include "renderthread/EglManager.h" #include "renderthread/Frame.h" +#include "utils/GLUtils.h" #include "utils/TraceUtils.h" #include <GrBackendSurface.h> @@ -58,10 +59,10 @@ Frame SkiaOpenGLPipeline::getFrame() { } bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty, - const FrameBuilder::LightGeometry& lightGeometry, + const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds, bool opaque, bool wideColorGamut, - const BakedOpRenderer::LightInfo& lightInfo, + const LightInfo& lightInfo, const std::vector<sp<RenderNode>>& renderNodes, FrameInfoVisualizer* profiler) { mEglManager.damageFrame(frame, dirty); diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h index ef5d9347a3ea..04b68f5e3278 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h @@ -33,9 +33,9 @@ public: renderthread::MakeCurrentResult makeCurrent() override; renderthread::Frame getFrame() override; bool draw(const renderthread::Frame& frame, const SkRect& screenDirty, const SkRect& dirty, - const FrameBuilder::LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, + const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds, bool opaque, bool wideColorGamut, - const BakedOpRenderer::LightInfo& lightInfo, + const LightInfo& lightInfo, const std::vector<sp<RenderNode> >& renderNodes, FrameInfoVisualizer* profiler) override; bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty, diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index d66cba12ad99..988981d65775 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -81,9 +81,9 @@ void SkiaPipeline::onPrepareTree() { mVectorDrawables.clear(); } -void SkiaPipeline::renderLayers(const FrameBuilder::LightGeometry& lightGeometry, +void SkiaPipeline::renderLayers(const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, bool opaque, - bool wideColorGamut, const BakedOpRenderer::LightInfo& lightInfo) { + bool wideColorGamut, const LightInfo& lightInfo) { updateLighting(lightGeometry, lightInfo); ATRACE_NAME("draw layers"); renderVectorDrawableCache(); @@ -103,7 +103,6 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque, // as not to lose info on what portion is damaged if (CC_LIKELY(layerNode->getLayerSurface() != nullptr)) { SkASSERT(layerNode->getLayerSurface()); - SkASSERT(layerNode->getDisplayList()->isSkiaDL()); SkiaDisplayList* displayList = (SkiaDisplayList*)layerNode->getDisplayList(); if (!displayList || displayList->isEmpty()) { SkDEBUGF(("%p drawLayers(%s) : missing drawable", layerNode, layerNode->getName())); diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h index 38ad9c09a8aa..8c9c80381b84 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaPipeline.h @@ -17,7 +17,7 @@ #pragma once #include <SkSurface.h> -#include "FrameBuilder.h" +#include "Lighting.h" #include "hwui/AnimatedImageDrawable.h" #include "renderthread/CanvasContext.h" #include "renderthread/IRenderPipeline.h" @@ -42,9 +42,9 @@ public: void unpinImages() override; void onPrepareTree() override; - void renderLayers(const FrameBuilder::LightGeometry& lightGeometry, + void renderLayers(const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, bool opaque, bool wideColorGamut, - const BakedOpRenderer::LightInfo& lightInfo) override; + const LightInfo& lightInfo) override; bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator, bool wideColorGamut, ErrorHandler* errorHandler) override; @@ -97,8 +97,8 @@ public: return mLightCenter; } - static void updateLighting(const FrameBuilder::LightGeometry& lightGeometry, - const BakedOpRenderer::LightInfo& lightInfo) { + static void updateLighting(const LightGeometry& lightGeometry, + const LightInfo& lightInfo) { mLightRadius = lightGeometry.radius; mAmbientShadowAlpha = lightInfo.ambientShadowAlpha; mSpotShadowAlpha = lightInfo.spotShadowAlpha; diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index 5825060f902a..b2519fe59891 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -62,10 +62,10 @@ Frame SkiaVulkanPipeline::getFrame() { } bool SkiaVulkanPipeline::draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty, - const FrameBuilder::LightGeometry& lightGeometry, + const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds, bool opaque, bool wideColorGamut, - const BakedOpRenderer::LightInfo& lightInfo, + const LightInfo& lightInfo, const std::vector<sp<RenderNode>>& renderNodes, FrameInfoVisualizer* profiler) { sk_sp<SkSurface> backBuffer = mVkSurface->getBackBufferSurface(); diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h index 03b4c79f2beb..7806b42e03dc 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h @@ -31,9 +31,9 @@ public: renderthread::MakeCurrentResult makeCurrent() override; renderthread::Frame getFrame() override; bool draw(const renderthread::Frame& frame, const SkRect& screenDirty, const SkRect& dirty, - const FrameBuilder::LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, + const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds, bool opaque, bool wideColorGamut, - const BakedOpRenderer::LightInfo& lightInfo, + const LightInfo& lightInfo, const std::vector<sp<RenderNode> >& renderNodes, FrameInfoVisualizer* profiler) override; bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty, diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp index f173136d6f8d..a60e2b5c815e 100644 --- a/libs/hwui/renderstate/RenderState.cpp +++ b/libs/hwui/renderstate/RenderState.cpp @@ -18,6 +18,7 @@ #include "DeferredLayerUpdater.h" #include "GlLayer.h" #include "VkLayer.h" +#include "Snapshot.h" #include "renderthread/CanvasContext.h" #include "renderthread/EglManager.h" diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index aaef85a5b1bc..358b842cee20 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -16,10 +16,8 @@ #pragma once -#include "BakedOpDispatcher.h" -#include "BakedOpRenderer.h" #include "DamageAccumulator.h" -#include "FrameBuilder.h" +#include "Lighting.h" #include "FrameInfo.h" #include "FrameInfoVisualizer.h" #include "FrameMetricsReporter.h" @@ -231,8 +229,8 @@ private: bool mOpaque; bool mWideColorGamut = false; - BakedOpRenderer::LightInfo mLightInfo; - FrameBuilder::LightGeometry mLightGeometry = {{0, 0, 0}, 0}; + LightInfo mLightInfo; + LightGeometry mLightGeometry = {{0, 0, 0}, 0}; bool mHaveNewSurface = false; DamageAccumulator mDamageAccumulator; diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h index d8c43e0d8ca8..0037a0f4306d 100644 --- a/libs/hwui/renderthread/DrawFrameTask.h +++ b/libs/hwui/renderthread/DrawFrameTask.h @@ -32,7 +32,6 @@ namespace android { namespace uirenderer { class DeferredLayerUpdater; -class DisplayList; class RenderNode; namespace renderthread { diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h index b1de49733c09..b94a7588a507 100644 --- a/libs/hwui/renderthread/IRenderPipeline.h +++ b/libs/hwui/renderthread/IRenderPipeline.h @@ -17,7 +17,10 @@ #pragma once #include "FrameInfoVisualizer.h" +#include "LayerUpdateQueue.h" #include "SwapBehavior.h" +#include "hwui/Bitmap.h" +#include "thread/TaskManager.h" #include <SkRect.h> #include <utils/RefBase.h> @@ -50,9 +53,9 @@ public: virtual MakeCurrentResult makeCurrent() = 0; virtual Frame getFrame() = 0; virtual bool draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty, - const FrameBuilder::LightGeometry& lightGeometry, + const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds, - bool opaque, bool wideColorGamut, const BakedOpRenderer::LightInfo& lightInfo, + bool opaque, bool wideColorGamut, const LightInfo& lightInfo, const std::vector<sp<RenderNode>>& renderNodes, FrameInfoVisualizer* profiler) = 0; virtual bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty, @@ -64,9 +67,9 @@ public: virtual bool isSurfaceReady() = 0; virtual bool isContextReady() = 0; virtual void onDestroyHardwareResources() = 0; - virtual void renderLayers(const FrameBuilder::LightGeometry& lightGeometry, + virtual void renderLayers(const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, bool opaque, bool wideColorGamut, - const BakedOpRenderer::LightInfo& lightInfo) = 0; + const LightInfo& lightInfo) = 0; virtual TaskManager* getTaskManager() = 0; virtual bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator, bool wideColorGamut, ErrorHandler* errorHandler) = 0; diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 0caf59b42202..4d1e1e8fb04b 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -66,10 +66,7 @@ void RenderProxy::setSwapBehavior(SwapBehavior swapBehavior) { bool RenderProxy::loadSystemProperties() { return mRenderThread.queue().runSync([this]() -> bool { - bool needsRedraw = false; - if (Caches::hasInstance()) { - needsRedraw = Properties::load(); - } + bool needsRedraw = Properties::load(); if (mContext->profiler().consumeProperties()) { needsRedraw = true; } diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index ca5a88bc5904..13fb5285bc31 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -29,7 +29,6 @@ #include <renderthread/RenderThread.h> #include <RecordedOp.h> -#include <RecordingCanvas.h> #include <memory> @@ -335,16 +334,10 @@ private: } auto displayList = node->getDisplayList(); if (displayList) { - if (displayList->isSkiaDL()) { - for (auto&& childDr : static_cast<skiapipeline::SkiaDisplayList*>( - const_cast<DisplayList*>(displayList)) - ->mChildNodes) { - syncHierarchyPropertiesAndDisplayListImpl(childDr.getRenderNode()); - } - } else { - for (auto&& childOp : displayList->getChildren()) { - syncHierarchyPropertiesAndDisplayListImpl(childOp->renderNode); - } + for (auto&& childDr : static_cast<skiapipeline::SkiaDisplayList*>( + const_cast<DisplayList*>(displayList)) + ->mChildNodes) { + syncHierarchyPropertiesAndDisplayListImpl(childDr.getRenderNode()); } } } diff --git a/libs/hwui/tests/common/scenes/TestSceneBase.h b/libs/hwui/tests/common/scenes/TestSceneBase.h index 792312a6a7a4..6f76a502ae3e 100644 --- a/libs/hwui/tests/common/scenes/TestSceneBase.h +++ b/libs/hwui/tests/common/scenes/TestSceneBase.h @@ -16,7 +16,7 @@ #pragma once -#include "RecordingCanvas.h" +#include "hwui/Canvas.h" #include "RenderNode.h" #include "tests/common/TestContext.h" #include "tests/common/TestScene.h" diff --git a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp index 0aaf7731c927..9388c2062736 100644 --- a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp +++ b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp @@ -16,8 +16,10 @@ #include <benchmark/benchmark.h> +#include "CanvasState.h" #include "DisplayList.h" -#include "RecordingCanvas.h" +#include "hwui/Canvas.h" +#include "pipeline/skia/SkiaDisplayList.h" #include "tests/common/TestUtils.h" using namespace android; @@ -25,7 +27,7 @@ using namespace android::uirenderer; void BM_DisplayList_alloc(benchmark::State& benchState) { while (benchState.KeepRunning()) { - auto displayList = new DisplayList(); + auto displayList = new skiapipeline::SkiaDisplayList(); benchmark::DoNotOptimize(displayList); delete displayList; } @@ -34,7 +36,7 @@ BENCHMARK(BM_DisplayList_alloc); void BM_DisplayList_alloc_theoretical(benchmark::State& benchState) { while (benchState.KeepRunning()) { - auto displayList = new char[sizeof(DisplayList)]; + auto displayList = new char[sizeof(skiapipeline::SkiaDisplayList)]; benchmark::DoNotOptimize(displayList); delete[] displayList; } diff --git a/libs/hwui/tests/microbench/FrameBuilderBench.cpp b/libs/hwui/tests/microbench/FrameBuilderBench.cpp deleted file mode 100644 index b6217665d743..000000000000 --- a/libs/hwui/tests/microbench/FrameBuilderBench.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <benchmark/benchmark.h> - -#include "BakedOpDispatcher.h" -#include "BakedOpRenderer.h" -#include "BakedOpState.h" -#include "FrameBuilder.h" -#include "LayerUpdateQueue.h" -#include "RecordedOp.h" -#include "RecordingCanvas.h" -#include "Vector.h" -#include "tests/common/TestContext.h" -#include "tests/common/TestScene.h" -#include "tests/common/TestUtils.h" - -#include <vector> - -using namespace android; -using namespace android::uirenderer; -using namespace android::uirenderer::renderthread; -using namespace android::uirenderer::test; - -const FrameBuilder::LightGeometry sLightGeometry = {{100, 100, 100}, 50}; -const BakedOpRenderer::LightInfo sLightInfo = {128, 128}; - -static sp<RenderNode> createTestNode() { - auto node = TestUtils::createNode<RecordingCanvas>( - 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { - sk_sp<Bitmap> bitmap(TestUtils::createBitmap(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(SaveFlags::MatrixClip); - 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); - return node; -} - -void BM_FrameBuilder_defer(benchmark::State& state) { - TestUtils::runOnRenderThread([&state](RenderThread& thread) { - auto node = createTestNode(); - while (state.KeepRunning()) { - FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200, sLightGeometry, - Caches::getInstance()); - frameBuilder.deferRenderNode(*node); - benchmark::DoNotOptimize(&frameBuilder); - } - }); -} -BENCHMARK(BM_FrameBuilder_defer); - -void BM_FrameBuilder_deferAndRender(benchmark::State& state) { - TestUtils::runOnRenderThread([&state](RenderThread& thread) { - auto node = createTestNode(); - - RenderState& renderState = thread.renderState(); - Caches& caches = Caches::getInstance(); - - while (state.KeepRunning()) { - FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200, sLightGeometry, caches); - frameBuilder.deferRenderNode(*node); - - BakedOpRenderer renderer(caches, renderState, true, false, sLightInfo); - frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer); - benchmark::DoNotOptimize(&renderer); - } - }); -} -BENCHMARK(BM_FrameBuilder_deferAndRender); - -static sp<RenderNode> getSyncedSceneNode(const char* sceneName) { - gDisplay = getBuiltInDisplay(); // switch to real display if present - - TestContext testContext; - TestScene::Options opts; - std::unique_ptr<TestScene> scene(TestScene::testMap()[sceneName].createScene(opts)); - - sp<RenderNode> rootNode = TestUtils::createNode<RecordingCanvas>( - 0, 0, gDisplay.w, gDisplay.h, - [&scene](RenderProperties& props, RecordingCanvas& canvas) { - scene->createContent(gDisplay.w, gDisplay.h, canvas); - }); - - TestUtils::syncHierarchyPropertiesAndDisplayList(rootNode); - return rootNode; -} - -static auto SCENES = { - "listview", -}; - -void BM_FrameBuilder_defer_scene(benchmark::State& state) { - TestUtils::runOnRenderThread([&state](RenderThread& thread) { - const char* sceneName = *(SCENES.begin() + state.range(0)); - state.SetLabel(sceneName); - auto node = getSyncedSceneNode(sceneName); - while (state.KeepRunning()) { - FrameBuilder frameBuilder(SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, - gDisplay.h, sLightGeometry, Caches::getInstance()); - frameBuilder.deferRenderNode(*node); - benchmark::DoNotOptimize(&frameBuilder); - } - }); -} -BENCHMARK(BM_FrameBuilder_defer_scene)->DenseRange(0, SCENES.size() - 1); - -void BM_FrameBuilder_deferAndRender_scene(benchmark::State& state) { - TestUtils::runOnRenderThread([&state](RenderThread& thread) { - const char* sceneName = *(SCENES.begin() + state.range(0)); - state.SetLabel(sceneName); - auto node = getSyncedSceneNode(sceneName); - - RenderState& renderState = thread.renderState(); - Caches& caches = Caches::getInstance(); - - while (state.KeepRunning()) { - FrameBuilder frameBuilder(SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, - gDisplay.h, sLightGeometry, Caches::getInstance()); - frameBuilder.deferRenderNode(*node); - - BakedOpRenderer renderer(caches, renderState, true, false, sLightInfo); - frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer); - benchmark::DoNotOptimize(&renderer); - } - }); -} -BENCHMARK(BM_FrameBuilder_deferAndRender_scene)->DenseRange(0, SCENES.size() - 1); diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp index 2e4de0bac755..634ceffe0741 100644 --- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp +++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp @@ -16,7 +16,6 @@ #include "tests/common/TestUtils.h" -#include <RecordingCanvas.h> #include <SkBlurDrawLooper.h> #include <SkCanvasStateUtils.h> #include <SkPicture.h> diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp index 42a92fcc7d89..26b6000a91e6 100644 --- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp +++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp @@ -167,10 +167,10 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderLayer) { ASSERT_EQ(layerUpdateQueue.entries().size(), 2UL); bool opaque = true; - FrameBuilder::LightGeometry lightGeometry; + LightGeometry lightGeometry; lightGeometry.radius = 1.0f; lightGeometry.center = {0.0f, 0.0f, 0.0f}; - BakedOpRenderer::LightInfo lightInfo; + LightInfo lightInfo; auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); pipeline->renderLayers(lightGeometry, &layerUpdateQueue, opaque, false, lightInfo); ASSERT_EQ(TestUtils::getColor(surfaceLayer1, 0, 0), SK_ColorRED); diff --git a/libs/hwui/utils/TestWindowContext.cpp b/libs/hwui/utils/TestWindowContext.cpp index 700d3b3cf000..8ac6e1f2d39a 100644 --- a/libs/hwui/utils/TestWindowContext.cpp +++ b/libs/hwui/utils/TestWindowContext.cpp @@ -17,7 +17,6 @@ #include "AnimationContext.h" #include "IContextFactory.h" -#include "RecordingCanvas.h" #include "RenderNode.h" #include "SkTypes.h" #include "gui/BufferQueue.h" @@ -25,6 +24,7 @@ #include "gui/IGraphicBufferConsumer.h" #include "gui/IGraphicBufferProducer.h" #include "gui/Surface.h" +#include "hwui/Canvas.h" #include "renderthread/RenderProxy.h" #include <cutils/memory.h> @@ -81,7 +81,7 @@ public: android::uirenderer::Vector3 lightVector{lightX, -200.0f, 800.0f}; mProxy->setup(800.0f, 255 * 0.075f, 255 * 0.15f); mProxy->setLightCenter(lightVector); - mCanvas.reset(new android::uirenderer::RecordingCanvas(mSize.width(), mSize.height())); + mCanvas.reset(Canvas::create_recording_canvas(mSize.width(), mSize.height(), mRootNode.get())); } SkCanvas* prepareToDraw() { @@ -155,7 +155,7 @@ public: private: std::unique_ptr<android::uirenderer::RenderNode> mRootNode; std::unique_ptr<android::uirenderer::renderthread::RenderProxy> mProxy; - std::unique_ptr<android::uirenderer::RecordingCanvas> mCanvas; + std::unique_ptr<android::Canvas> mCanvas; android::sp<android::IGraphicBufferProducer> mProducer; android::sp<android::IGraphicBufferConsumer> mConsumer; android::sp<android::CpuConsumer> mCpuConsumer; |