/* * 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_RECORDED_OP_H #define ANDROID_HWUI_RECORDED_OP_H #include "RecordedOp.h" #include "font/FontUtil.h" #include "Matrix.h" #include "Rect.h" #include "RenderNode.h" #include "TessellationCache.h" #include "utils/LinearAllocator.h" #include "Vector.h" #include <androidfw/ResourceTypes.h> #include <SkXfermode.h> class SkBitmap; class SkPaint; namespace android { namespace uirenderer { struct ClipBase; class OffscreenBuffer; class RenderNode; struct Vertex; namespace VectorDrawable { class Tree; } /** * Authoritative op list, used for generating the op ID enum, ID based LUTS, and * the functions to which they dispatch. Parameter macros are executed for each op, * in order, based on the op's type. * * There are 4 types of op, which defines dispatch/LUT capability: * * | DisplayList | Render | Merge | * -------------|-------------|-------------|-------------| * PRE RENDER | Yes | | | * RENDER ONLY | | Yes | | * UNMERGEABLE | Yes | Yes | | * MERGEABLE | Yes | Yes | Yes | * * PRE RENDER - These ops are recorded into DisplayLists, but can't be directly rendered. This * may be because they need to be transformed into other op types (e.g. CirclePropsOp), * be traversed to access multiple renderable ops within (e.g. RenderNodeOp), or because they * modify renderbuffer lifecycle, instead of directly rendering content (the various LayerOps). * * RENDER ONLY - These ops cannot be recorded into DisplayLists, and are instead implicitly * constructed from other commands/RenderNode properties. They cannot be merged. * * UNMERGEABLE - These ops can be recorded into DisplayLists and rendered directly, but do not * support merged rendering. * * MERGEABLE - These ops can be recorded into DisplayLists and rendered individually, or merged * under certain circumstances. */ #define MAP_OPS_BASED_ON_TYPE(PRE_RENDER_OP_FN, RENDER_ONLY_OP_FN, UNMERGEABLE_OP_FN, MERGEABLE_OP_FN) \ PRE_RENDER_OP_FN(RenderNodeOp) \ PRE_RENDER_OP_FN(CirclePropsOp) \ PRE_RENDER_OP_FN(RoundRectPropsOp) \ PRE_RENDER_OP_FN(BeginLayerOp) \ PRE_RENDER_OP_FN(EndLayerOp) \ PRE_RENDER_OP_FN(BeginUnclippedLayerOp) \ PRE_RENDER_OP_FN(EndUnclippedLayerOp) \ PRE_RENDER_OP_FN(VectorDrawableOp) \ \ RENDER_ONLY_OP_FN(ShadowOp) \ RENDER_ONLY_OP_FN(LayerOp) \ RENDER_ONLY_OP_FN(CopyToLayerOp) \ RENDER_ONLY_OP_FN(CopyFromLayerOp) \ \ UNMERGEABLE_OP_FN(ArcOp) \ UNMERGEABLE_OP_FN(BitmapMeshOp) \ UNMERGEABLE_OP_FN(BitmapRectOp) \ UNMERGEABLE_OP_FN(ColorOp) \ UNMERGEABLE_OP_FN(FunctorOp) \ UNMERGEABLE_OP_FN(LinesOp) \ UNMERGEABLE_OP_FN(OvalOp) \ UNMERGEABLE_OP_FN(PathOp) \ UNMERGEABLE_OP_FN(PointsOp) \ UNMERGEABLE_OP_FN(RectOp) \ UNMERGEABLE_OP_FN(RoundRectOp) \ UNMERGEABLE_OP_FN(SimpleRectsOp) \ UNMERGEABLE_OP_FN(TextOnPathOp) \ UNMERGEABLE_OP_FN(TextureLayerOp) \ \ MERGEABLE_OP_FN(BitmapOp) \ MERGEABLE_OP_FN(PatchOp) \ MERGEABLE_OP_FN(TextOp) /** * LUT generators, which will insert nullptr for unsupported ops */ #define NULLPTR_OP_FN(Type) nullptr, #define BUILD_DEFERRABLE_OP_LUT(OP_FN) \ { MAP_OPS_BASED_ON_TYPE(OP_FN, NULLPTR_OP_FN, OP_FN, OP_FN) } #define BUILD_MERGEABLE_OP_LUT(OP_FN) \ { MAP_OPS_BASED_ON_TYPE(NULLPTR_OP_FN, NULLPTR_OP_FN, NULLPTR_OP_FN, OP_FN) } #define BUILD_RENDERABLE_OP_LUT(OP_FN) \ { MAP_OPS_BASED_ON_TYPE(NULLPTR_OP_FN, OP_FN, OP_FN, OP_FN) } #define BUILD_FULL_OP_LUT(OP_FN) \ { MAP_OPS_BASED_ON_TYPE(OP_FN, OP_FN, OP_FN, OP_FN) } /** * Op mapping functions, which skip unsupported ops. * * Note: Do not use for LUTS, since these do not preserve ID order. */ #define NULL_OP_FN(Type) #define MAP_DEFERRABLE_OPS(OP_FN) \ MAP_OPS_BASED_ON_TYPE(OP_FN, NULL_OP_FN, OP_FN, OP_FN) #define MAP_MERGEABLE_OPS(OP_FN) \ MAP_OPS_BASED_ON_TYPE(NULL_OP_FN, NULL_OP_FN, NULL_OP_FN, OP_FN) #define MAP_RENDERABLE_OPS(OP_FN) \ MAP_OPS_BASED_ON_TYPE(NULL_OP_FN, OP_FN, OP_FN, OP_FN) // Generate OpId enum #define IDENTITY_FN(Type) Type, namespace RecordedOpId { enum { MAP_OPS_BASED_ON_TYPE(IDENTITY_FN, IDENTITY_FN, IDENTITY_FN, IDENTITY_FN) Count, }; } static_assert(RecordedOpId::RenderNodeOp == 0, "First index must be zero for LUTs to work"); #define BASE_PARAMS const Rect& unmappedBounds, const Matrix4& localMatrix, const ClipBase* localClip, const SkPaint* paint #define BASE_PARAMS_PAINTLESS const Rect& unmappedBounds, const Matrix4& localMatrix, const ClipBase* localClip #define SUPER(Type) RecordedOp(RecordedOpId::Type, unmappedBounds, localMatrix, localClip, paint) #define SUPER_PAINTLESS(Type) RecordedOp(RecordedOpId::Type, unmappedBounds, localMatrix, localClip, nullptr) struct RecordedOp { /* ID from RecordedOpId - generally used for jumping into function tables */ const int opId; /* bounds in *local* space, without accounting for DisplayList transformation, or stroke */ const Rect unmappedBounds; /* transform in recording space (vs DisplayList origin) */ const Matrix4 localMatrix; /* clip in recording space - nullptr if not clipped */ const ClipBase* localClip; /* optional paint, stored in base object to simplify merging logic */ const SkPaint* paint; protected: RecordedOp(unsigned int opId, BASE_PARAMS) : opId(opId) , unmappedBounds(unmappedBounds) , localMatrix(localMatrix) , localClip(localClip) , paint(paint) {} }; struct RenderNodeOp : RecordedOp { RenderNodeOp(BASE_PARAMS_PAINTLESS, RenderNode* renderNode) : SUPER_PAINTLESS(RenderNodeOp) , renderNode(renderNode) {} RenderNode * renderNode; // not const, since drawing modifies it /** * Holds the transformation between the projection surface ViewGroup and this RenderNode * drawing instance. Represents any translations / transformations done within the drawing of * the compositing ancestor ViewGroup's draw, before the draw of the View represented by this * DisplayList draw instance. * * Note: doesn't include transformation within the RenderNode, or its properties. */ Matrix4 transformFromCompositingAncestor; bool skipInOrderDraw = false; }; //////////////////////////////////////////////////////////////////////////////////////////////////// // Standard Ops //////////////////////////////////////////////////////////////////////////////////////////////////// struct ArcOp : RecordedOp { ArcOp(BASE_PARAMS, float startAngle, float sweepAngle, bool useCenter) : SUPER(ArcOp) , startAngle(startAngle) , sweepAngle(sweepAngle) , useCenter(useCenter) {} const float startAngle; const float sweepAngle; const bool useCenter; }; struct BitmapOp : RecordedOp { BitmapOp(BASE_PARAMS, const SkBitmap* bitmap) : SUPER(BitmapOp) , bitmap(bitmap) {} const SkBitmap* bitmap; // TODO: asset atlas/texture id lookup? }; struct BitmapMeshOp : RecordedOp { BitmapMeshOp(BASE_PARAMS, const SkBitmap* bitmap, int meshWidth, int meshHeight, const float* vertices, const int* colors) : SUPER(BitmapMeshOp) , bitmap(bitmap) , meshWidth(meshWidth) , meshHeight(meshHeight) , vertices(vertices) , colors(colors) {} const SkBitmap* bitmap; const int meshWidth; const int meshHeight; const float* vertices; const int* colors; }; struct BitmapRectOp : RecordedOp { BitmapRectOp(BASE_PARAMS, const SkBitmap* bitmap, const Rect& src) : SUPER(BitmapRectOp) , bitmap(bitmap) , src(src) {} const SkBitmap* bitmap; const Rect src; }; struct CirclePropsOp : RecordedOp { CirclePropsOp(const Matrix4& localMatrix, const ClipBase* localClip, const SkPaint* paint, float* x, float* y, float* radius) : RecordedOp(RecordedOpId::CirclePropsOp, Rect(), localMatrix, localClip, paint) , x(x) , y(y) , radius(radius) {} const float* x; const float* y; const float* radius; }; struct ColorOp : RecordedOp { // Note: unbounded op that will fillclip, so no bounds/matrix needed ColorOp(const ClipBase* localClip, int color, SkXfermode::Mode mode) : RecordedOp(RecordedOpId::ColorOp, Rect(), Matrix4::identity(), localClip, nullptr) , color(color) , mode(mode) {} const int color; const SkXfermode::Mode mode; }; struct FunctorOp : RecordedOp { // Note: undefined record-time bounds, since this op fills the clip // TODO: explicitly define bounds FunctorOp(const Matrix4& localMatrix, const ClipBase* localClip, Functor* functor) : RecordedOp(RecordedOpId::FunctorOp, Rect(), localMatrix, localClip, nullptr) , functor(functor) {} Functor* functor; }; struct LinesOp : RecordedOp { LinesOp(BASE_PARAMS, const float* points, const int floatCount) : SUPER(LinesOp) , points(points) , floatCount(floatCount) {} const float* points; const int floatCount; }; struct OvalOp : RecordedOp { OvalOp(BASE_PARAMS) : SUPER(OvalOp) {} }; struct PatchOp : RecordedOp { PatchOp(BASE_PARAMS, const SkBitmap* bitmap, const Res_png_9patch* patch) : SUPER(PatchOp) , bitmap(bitmap) , patch(patch) {} const SkBitmap* bitmap; const Res_png_9patch* patch; }; struct PathOp : RecordedOp { PathOp(BASE_PARAMS, const SkPath* path) : SUPER(PathOp) , path(path) {} const SkPath* path; }; struct PointsOp : RecordedOp { PointsOp(BASE_PARAMS, const float* points, const int floatCount) : SUPER(PointsOp) , points(points) , floatCount(floatCount) {} const float* points; const int floatCount; }; struct RectOp : RecordedOp { RectOp(BASE_PARAMS) : SUPER(RectOp) {} }; struct RoundRectOp : RecordedOp { RoundRectOp(BASE_PARAMS, float rx, float ry) : SUPER(RoundRectOp) , rx(rx) , ry(ry) {} const float rx; const float ry; }; struct RoundRectPropsOp : RecordedOp { RoundRectPropsOp(const Matrix4& localMatrix, const ClipBase* localClip, const SkPaint* paint, float* left, float* top, float* right, float* bottom, float *rx, float *ry) : RecordedOp(RecordedOpId::RoundRectPropsOp, Rect(), localMatrix, localClip, paint) , left(left) , top(top) , right(right) , bottom(bottom) , rx(rx) , ry(ry) {} const float* left; const float* top; const float* right; const float* bottom; const float* rx; const float* ry; }; struct VectorDrawableOp : RecordedOp { VectorDrawableOp(VectorDrawable::Tree* tree, BASE_PARAMS_PAINTLESS) : SUPER_PAINTLESS(VectorDrawableOp) , vectorDrawable(tree) {} VectorDrawable::Tree* vectorDrawable; }; /** * Real-time, dynamic-lit shadow. * * Uses invalid/empty bounds and matrix since ShadowOp bounds aren't known at defer time, * and are resolved dynamically, and transform isn't needed. * * State construction handles these properties specially, ignoring matrix/bounds. */ struct ShadowOp : RecordedOp { ShadowOp(sp<TessellationCache::ShadowTask>& shadowTask, float casterAlpha) : RecordedOp(RecordedOpId::ShadowOp, Rect(), Matrix4::identity(), nullptr, nullptr) , shadowTask(shadowTask) , casterAlpha(casterAlpha) { }; sp<TessellationCache::ShadowTask> shadowTask; const float casterAlpha; }; struct SimpleRectsOp : RecordedOp { // Filled, no AA (TODO: better name?) SimpleRectsOp(BASE_PARAMS, Vertex* vertices, size_t vertexCount) : SUPER(SimpleRectsOp) , vertices(vertices) , vertexCount(vertexCount) {} Vertex* vertices; const size_t vertexCount; }; struct TextOp : RecordedOp { TextOp(BASE_PARAMS, const glyph_t* glyphs, const float* positions, int glyphCount, float x, float y) : SUPER(TextOp) , glyphs(glyphs) , positions(positions) , glyphCount(glyphCount) , x(x) , y(y) {} const glyph_t* glyphs; const float* positions; const int glyphCount; const float x; const float y; }; struct TextOnPathOp : RecordedOp { // TODO: explicitly define bounds TextOnPathOp(const Matrix4& localMatrix, const ClipBase* localClip, const SkPaint* paint, const glyph_t* glyphs, int glyphCount, const SkPath* path, float hOffset, float vOffset) : RecordedOp(RecordedOpId::TextOnPathOp, Rect(), localMatrix, localClip, paint) , glyphs(glyphs) , glyphCount(glyphCount) , path(path) , hOffset(hOffset) , vOffset(vOffset) {} const glyph_t* glyphs; const int glyphCount; const SkPath* path; const float hOffset; const float vOffset; }; struct TextureLayerOp : RecordedOp { TextureLayerOp(BASE_PARAMS_PAINTLESS, Layer* layer) : SUPER_PAINTLESS(TextureLayerOp) , layer(layer) {} // Copy an existing TextureLayerOp, replacing the underlying matrix TextureLayerOp(const TextureLayerOp& op, const Matrix4& replacementMatrix) : RecordedOp(RecordedOpId::TextureLayerOp, op.unmappedBounds, replacementMatrix, op.localClip, op.paint) , layer(op.layer) { } Layer* layer; }; //////////////////////////////////////////////////////////////////////////////////////////////////// // Layers //////////////////////////////////////////////////////////////////////////////////////////////////// /** * Stateful operation! denotes the creation of an off-screen layer, * and that commands following will render into it. */ struct BeginLayerOp : RecordedOp { BeginLayerOp(BASE_PARAMS) : SUPER(BeginLayerOp) {} }; /** * Stateful operation! Denotes end of off-screen layer, and that * commands since last BeginLayerOp should be drawn into parent FBO. * * State in this op is empty, it just serves to signal that a layer has been finished. */ struct EndLayerOp : RecordedOp { EndLayerOp() : RecordedOp(RecordedOpId::EndLayerOp, Rect(), Matrix4::identity(), nullptr, nullptr) {} }; struct BeginUnclippedLayerOp : RecordedOp { BeginUnclippedLayerOp(BASE_PARAMS) : SUPER(BeginUnclippedLayerOp) {} }; struct EndUnclippedLayerOp : RecordedOp { EndUnclippedLayerOp() : RecordedOp(RecordedOpId::EndUnclippedLayerOp, Rect(), Matrix4::identity(), nullptr, nullptr) {} }; struct CopyToLayerOp : RecordedOp { CopyToLayerOp(const RecordedOp& op, OffscreenBuffer** layerHandle) : RecordedOp(RecordedOpId::CopyToLayerOp, op.unmappedBounds, op.localMatrix, nullptr, // clip intentionally ignored op.paint) , layerHandle(layerHandle) {} // Records a handle to the Layer object, since the Layer itself won't be // constructed until after this operation is constructed. OffscreenBuffer** layerHandle; }; // draw the parameter layer underneath struct CopyFromLayerOp : RecordedOp { CopyFromLayerOp(const RecordedOp& op, OffscreenBuffer** layerHandle) : RecordedOp(RecordedOpId::CopyFromLayerOp, op.unmappedBounds, op.localMatrix, nullptr, // clip intentionally ignored op.paint) , layerHandle(layerHandle) {} // Records a handle to the Layer object, since the Layer itself won't be // constructed until after this operation is constructed. OffscreenBuffer** layerHandle; }; /** * Draws an OffscreenBuffer. * * Alpha, mode, and colorfilter are embedded, since LayerOps are always dynamically generated, * when creating/tracking a SkPaint* during defer isn't worth the bother. */ struct LayerOp : RecordedOp { // Records a one-use (saveLayer) layer for drawing. LayerOp(BASE_PARAMS, OffscreenBuffer** layerHandle) : SUPER_PAINTLESS(LayerOp) , layerHandle(layerHandle) , alpha(paint ? paint->getAlpha() / 255.0f : 1.0f) , mode(PaintUtils::getXfermodeDirect(paint)) , colorFilter(paint ? paint->getColorFilter() : nullptr) {} LayerOp(RenderNode& node) : RecordedOp(RecordedOpId::LayerOp, Rect(node.getWidth(), node.getHeight()), Matrix4::identity(), nullptr, nullptr) , layerHandle(node.getLayerHandle()) , alpha(node.properties().layerProperties().alpha() / 255.0f) , mode(node.properties().layerProperties().xferMode()) , colorFilter(node.properties().layerProperties().colorFilter()) {} // Records a handle to the Layer object, since the Layer itself won't be // constructed until after this operation is constructed. OffscreenBuffer** layerHandle; const float alpha; const SkXfermode::Mode mode; // pointer to object owned by either LayerProperties, or a recorded Paint object in a // BeginLayerOp. Lives longer than LayerOp in either case, so no skia ref counting is used. SkColorFilter* colorFilter; }; }; // namespace uirenderer }; // namespace android #endif // ANDROID_HWUI_RECORDED_OP_H