• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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