1 /* 2 * Copyright 2021 Google LLC 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #ifndef skgpu_DrawList_DEFINED 9 #define skgpu_DrawList_DEFINED 10 11 #include "include/core/SkColor.h" 12 #include "include/core/SkPaint.h" 13 #include "include/private/SkTOptional.h" 14 #include "src/core/SkTBlockList.h" 15 16 #include "experimental/graphite/src/DrawOrder.h" 17 #include "experimental/graphite/src/geom/Shape.h" 18 #include "experimental/graphite/src/geom/Transform_graphite.h" 19 20 #include <limits> 21 22 class SkPath; 23 class SkShader; 24 struct SkIRect; 25 26 namespace skgpu { 27 28 struct IndexWriter; 29 class Renderer; 30 struct VertexWriter; 31 32 // TBD: If occlusion culling is eliminated as a phase, we can easily move the paint conversion 33 // back to Device when the command is recorded (similar to SkPaint -> GrPaint), and then 34 // PaintParams is not required as an intermediate representation. 35 // NOTE: Only represents the shading state of an SkPaint. Style and complex effects (mask filters, 36 // image filters, path effects) must be handled higher up. AA is not tracked since everything is 37 // assumed to be anti-aliased. 38 class PaintParams { 39 public: 40 PaintParams(const SkColor4f& color, SkBlendMode, sk_sp<SkShader>); 41 PaintParams(const PaintParams&); 42 ~PaintParams(); 43 44 PaintParams& operator=(const PaintParams&); 45 color()46 SkColor4f color() const { return fColor; } blendMode()47 SkBlendMode blendMode() const { return fBlendMode; } shader()48 SkShader* shader() const { return fShader.get(); } 49 sk_sp<SkShader> refShader() const; 50 51 private: 52 SkColor4f fColor; 53 SkBlendMode fBlendMode; 54 sk_sp<SkShader> fShader; // For now only use SkShader::asAGradient() when converting to GPU 55 // TODO: Will also store ColorFilter, custom Blender, dither, and any extra shader from an 56 // active clipShader(). 57 }; 58 59 // NOTE: Only represents the stroke or hairline styles; stroke-and-fill must be handled higher up. 60 class StrokeParams { 61 public: StrokeParams()62 StrokeParams() : fHalfWidth(0.f), fJoinLimit(0.f), fCap(SkPaint::kButt_Cap) {} StrokeParams(float width,float miterLimit,SkPaint::Join join,SkPaint::Cap cap)63 StrokeParams(float width, 64 float miterLimit, 65 SkPaint::Join join, 66 SkPaint::Cap cap) 67 : fHalfWidth(std::max(0.f, 0.5f * width)) 68 , fJoinLimit(join == SkPaint::kMiter_Join ? std::max(0.f, miterLimit) : 69 (join == SkPaint::kBevel_Join ? 0.f : -1.f)) 70 , fCap(cap) {} 71 72 StrokeParams(const StrokeParams&) = default; 73 74 StrokeParams& operator=(const StrokeParams&) = default; 75 isMiterJoin()76 bool isMiterJoin() const { return fJoinLimit > 0.f; } isBevelJoin()77 bool isBevelJoin() const { return fJoinLimit == 0.f; } isRoundJoin()78 bool isRoundJoin() const { return fJoinLimit < 0.f; } 79 halfWidth()80 float halfWidth() const { return fHalfWidth; } width()81 float width() const { return 2.f * fHalfWidth; } miterLimit()82 float miterLimit() const { return std::max(0.f, fJoinLimit); } cap()83 SkPaint::Cap cap() const { return fCap; } join()84 SkPaint::Join join() const { 85 return fJoinLimit > 0.f ? SkPaint::kMiter_Join : 86 (fJoinLimit == 0.f ? SkPaint::kBevel_Join : SkPaint::kRound_Join); 87 } 88 89 private: 90 float fHalfWidth; // >0: relative to transform; ==0: hairline, 1px in device space 91 float fJoinLimit; // >0: miter join; ==0: bevel join; <0: round join 92 SkPaint::Cap fCap; 93 }; 94 95 // TBD: Separate DashParams extracted from an SkDashPathEffect? Or folded into StrokeParams? 96 97 class Clip { 98 public: Clip(const Rect & drawBounds,const SkIRect & scissor)99 Clip(const Rect& drawBounds, const SkIRect& scissor) 100 : fDrawBounds(drawBounds) 101 , fScissor(scissor) {} 102 drawBounds()103 const Rect& drawBounds() const { return fDrawBounds; } scissor()104 const SkIRect& scissor() const { return fScissor; } 105 106 private: 107 // Draw bounds represent the tight bounds of the draw, including any padding/outset for stroking 108 // and intersected with the scissor. 109 // - DrawList assumes the DrawBounds are correct for a given shape, transform, and style. They 110 // are provided to the DrawList to avoid re-calculating the same bounds. 111 Rect fDrawBounds; 112 // The scissor must contain fDrawBounds, and must already be intersected with the device bounds. 113 SkIRect fScissor; 114 // TODO: If we add more complex analytic shapes for clipping, e.g. coverage rrect, it should 115 // go here. 116 }; 117 118 /** 119 * A DrawList represents a collection of drawing commands (and related clip/shading state) in 120 * a form that closely mirrors what can be rendered efficiently and directly by the GPU backend 121 * (while balancing how much pre-processing to do for draws that might get eliminated later due to 122 * occlusion culling). 123 * 124 * A draw command combines: 125 * - a shape 126 * - a transform 127 * - a primitive clip (not affected by the transform) 128 * - optional shading description (shader, color filter, blend mode, etc) 129 * - a draw ordering (compressed painters index, stencil set, and write/test depth) 130 * 131 * Commands are accumulated in an arbitrary order and then sorted by increasing sort z when the list 132 * is prepared into an actual command buffer. The result of a draw command is the rasterization of 133 * the transformed shape, restricted by its primitive clip (e.g. a scissor rect) and a depth test 134 * of "GREATER" vs. its write/test z. (A test of GREATER, as opposed to GEQUAL, avoids double hits 135 * for draws that may have overlapping geometry, e.g. stroking.) If the command has a shading 136 * description, the color buffer will be modified; if not, it will be a depth-only draw. 137 * 138 * In addition to sorting the collected commands, the command list can be optimized during 139 * preparation. Commands that are fully occluded by later operations can be skipped entirely without 140 * affecting the final results. Adjacent commands (post sort) that would use equivalent GPU 141 * pipelines are merged to produce fewer (but larger) operations on the GPU. 142 * 143 * Other than flush-time optimizations (sort, cull, and merge), the command list does what you tell 144 * it to. Draw-specific simplification, style application, and advanced clipping should be handled 145 * at a higher layer. 146 */ 147 class DrawList { 148 public: 149 // The maximum number of draw calls that can be recorded into a DrawList before it must be 150 // converted to a DrawPass. The true fundamental limit is imposed by the limits of the depth 151 // attachment and precision of CompressedPaintersOrder and PaintDepth. These values can be 152 // shared by multiple draw calls so it's more difficult to reason about how much room is left 153 // in a DrawList. Limiting it to this keeps tracking simple and ensures that the sequences in 154 // DrawOrder cannot overflow since they are always less than or equal to the number of draws. 155 static constexpr int kMaxDraws = std::numeric_limits<uint16_t>::max(); 156 157 // NOTE: All path rendering functions, e.g. [fill|stroke|...]Path() that take a Shape 158 // draw using the same underlying techniques regardless of the shape's type. If a Shape has 159 // a type matching a simpler primitive technique or coverage AA, the caller must explicitly 160 // invoke it to use that rendering algorithms. 161 // 162 // Additionally, DrawList requires that all Transforms passed to its draw calls be valid and 163 // assert as much; invalid transforms should be detected at the Device level or similar. 164 165 void stencilAndFillPath(const Transform& localToDevice, 166 const Shape& shape, 167 const Clip& clip, 168 DrawOrder ordering, 169 const PaintParams* paint); 170 171 void fillConvexPath(const Transform& localToDevice, 172 const Shape& shape, 173 const Clip& clip, 174 DrawOrder ordering, 175 const PaintParams* paint); 176 177 void strokePath(const Transform& localToDevice, 178 const Shape& shape, 179 const StrokeParams& stroke, 180 const Clip& clip, 181 DrawOrder ordering, 182 const PaintParams* paint); 183 184 // TODO: fill[R]Rect, stroke[R]Rect (will need to support per-edge aa and arbitrary quads) 185 // fillImage (per-edge aa and arbitrary quad, only if this fast path is required) 186 // dashPath(feasible for general paths?) 187 // dash[R]Rect(only if general dashPath isn't viable) 188 // dashLine(only if general or rrect version aren't viable) 189 drawCount()190 int drawCount() const { return fDraws.count(); } renderStepCount()191 int renderStepCount() const { return fRenderStepCount; } 192 193 private: 194 friend class DrawPass; 195 196 struct Draw { 197 const Renderer& fRenderer; // Statically defined by function that recorded the Draw 198 const Transform& fTransform; // Points to a transform in fTransforms 199 200 Shape fShape; 201 Clip fClip; 202 DrawOrder fOrder; 203 204 skstd::optional<PaintParams> fPaintParams; // Not present implies depth-only draw 205 skstd::optional<StrokeParams> fStrokeParams; // Not present implies fill 206 DrawDraw207 Draw(const Renderer& renderer, const Transform& transform, const Shape& shape, 208 const Clip& clip, DrawOrder order, const PaintParams* paint, 209 const StrokeParams* stroke) 210 : fRenderer(renderer) 211 , fTransform(transform) 212 , fShape(shape) 213 , fClip(clip) 214 , fOrder(order) 215 , fPaintParams(paint ? skstd::optional<PaintParams>(*paint) : skstd::nullopt) 216 , fStrokeParams(stroke ? skstd::optional<StrokeParams>(*stroke) : skstd::nullopt) {} 217 218 size_t requiredVertexSpace(int renderStep) const; 219 size_t requiredIndexSpace(int renderStep) const; 220 221 void writeVertices(VertexWriter, IndexWriter, int renderStep) const; 222 }; 223 224 // The returned Transform reference remains valid for the lifetime of the DrawList. 225 const Transform& deduplicateTransform(const Transform&); 226 227 SkTBlockList<Transform, 16> fTransforms; 228 SkTBlockList<Draw, 16> fDraws; 229 230 // Running total of RenderSteps for all draws, assuming nothing is culled 231 int fRenderStepCount; 232 }; 233 234 } // namespace skgpu 235 236 #endif // skgpu_DrawList_DEFINED 237