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