• 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_graphite_Renderer_DEFINED
9 #define skgpu_graphite_Renderer_DEFINED
10 
11 #include "include/core/SkSpan.h"
12 #include "include/core/SkString.h"
13 #include "include/core/SkTypes.h"
14 #include "src/base/SkEnumBitMask.h"
15 #include "src/base/SkVx.h"
16 #include "src/gpu/graphite/Attribute.h"
17 #include "src/gpu/graphite/DrawTypes.h"
18 #include "src/gpu/graphite/ResourceTypes.h"
19 #include "src/gpu/graphite/Uniform.h"
20 
21 #include <array>
22 #include <initializer_list>
23 #include <string>
24 #include <string_view>
25 #include <vector>
26 
27 enum class SkPathFillType;
28 
29 namespace skgpu { enum class MaskFormat; }
30 
31 namespace skgpu::graphite {
32 
33 class DrawWriter;
34 class DrawParams;
35 class PipelineDataGatherer;
36 class Rect;
37 class ResourceProvider;
38 class TextureDataBlock;
39 class Transform;
40 
41 struct ResourceBindingRequirements;
42 
43 enum class Coverage { kNone, kSingleChannel, kLCD };
44 
45 struct Varying {
46     const char* fName;
47     SkSLType fType;
48     // TODO: add modifier (e.g., flat and noperspective) support
49 };
50 
51 /**
52  * The actual technique for rasterizing a high-level draw recorded in a DrawList is handled by a
53  * specific Renderer. Each technique has an associated singleton Renderer that decomposes the
54  * technique into a series of RenderSteps that must be executed in the specified order for the draw.
55  * However, the RenderStep executions for multiple draws can be re-arranged so batches of each
56  * step can be performed in a larger GPU operation. This re-arranging relies on accurate
57  * determination of the DisjointStencilIndex for each draw so that stencil steps are not corrupted
58  * by another draw before its cover step is executed. It also relies on the CompressedPaintersOrder
59  * for each draw to ensure steps are not re-arranged in a way that violates the original draw order.
60  *
61  * Renderer itself is non-virtual since it simply has to point to a list of RenderSteps. RenderSteps
62  * on the other hand are virtual implement the technique specific functionality. It is entirely
63  * possible for certain types of steps, e.g. a bounding box cover, to be re-used across different
64  * Renderers even if the preceeding steps were different.
65  *
66  * All Renderers are accessed through the SharedContext's RendererProvider.
67  */
68 class RenderStep {
69 public:
70     virtual ~RenderStep() = default;
71 
72     // The DrawWriter is configured with the vertex and instance strides of the RenderStep, and its
73     // primitive type. The recorded draws will be executed with a graphics pipeline compatible with
74     // this RenderStep.
75     virtual void writeVertices(DrawWriter*, const DrawParams&, skvx::ushort2 ssboIndices) const = 0;
76 
77     // Write out the uniform values (aligned for the layout), textures, and samplers. The uniform
78     // values will be de-duplicated across all draws using the RenderStep before uploading to the
79     // GPU, but it can be assumed the uniforms will be bound before the draws recorded in
80     // 'writeVertices' are executed.
81     virtual void writeUniformsAndTextures(const DrawParams&, PipelineDataGatherer*) const = 0;
82 
83     // Returns the body of a vertex function, which must define a float4 devPosition variable and
84     // must write to an already-defined float2 stepLocalCoords variable. This will be automatically
85     // set to a varying for the fragment shader if the paint requires local coords. This SkSL has
86     // access to the variables declared by vertexAttributes(), instanceAttributes(), and uniforms().
87     // The 'devPosition' variable's z must store the PaintDepth normalized to a float from [0, 1],
88     // for each processed draw although the RenderStep can choose to upload it in any manner.
89     //
90     // NOTE: The above contract is mainly so that the entire SkSL program can be created by just str
91     // concatenating struct definitions generated from the RenderStep and paint Combination
92     // and then including the function bodies returned here.
93     virtual std::string vertexSkSL() const = 0;
94 
95     // Emits code to set up textures and samplers. Should only be defined if hasTextures is true.
texturesAndSamplersSkSL(const ResourceBindingRequirements &,int * nextBindingIndex)96     virtual std::string texturesAndSamplersSkSL(const ResourceBindingRequirements&,
97                                                 int* nextBindingIndex) const {
98         return R"()";
99     }
100 
101     // Emits code to set up coverage value. Should only be defined if overridesCoverage is true.
102     // When implemented the returned SkSL fragment should write its coverage into a
103     // 'half4 outputCoverage' variable (defined in the calling code) with the actual
104     // coverage splatted out into all four channels.
fragmentCoverageSkSL()105     virtual const char* fragmentCoverageSkSL() const { return R"()"; }
106 
107     // Emits code to set up a primitive color value. Should only be defined if emitsPrimitiveColor
108     // is true. When implemented, the returned SkSL fragment should write its color into a
109     // 'half4 primitiveColor' variable (defined in the calling code).
fragmentColorSkSL()110     virtual const char* fragmentColorSkSL() const { return R"()"; }
111 
uniqueID()112     uint32_t uniqueID() const { return fUniqueID; }
113 
114     // Returns a name formatted as "Subclass[variant]", where "Subclass" matches the C++ class name
115     // and variant is a unique term describing instance's specific configuration.
name()116     const char* name() const { return fName.c_str(); }
117 
requiresMSAA()118     bool requiresMSAA()        const { return SkToBool(fFlags & Flags::kRequiresMSAA);        }
performsShading()119     bool performsShading()     const { return SkToBool(fFlags & Flags::kPerformsShading);     }
hasTextures()120     bool hasTextures()         const { return SkToBool(fFlags & Flags::kHasTextures);         }
emitsPrimitiveColor()121     bool emitsPrimitiveColor() const { return SkToBool(fFlags & Flags::kEmitsPrimitiveColor); }
outsetBoundsForAA()122     bool outsetBoundsForAA()   const { return SkToBool(fFlags & Flags::kOutsetBoundsForAA);   }
123 
coverage()124     Coverage coverage() const { return RenderStep::GetCoverage(fFlags); }
125 
primitiveType()126     PrimitiveType primitiveType()  const { return fPrimitiveType;  }
vertexStride()127     size_t        vertexStride()   const { return fVertexStride;   }
instanceStride()128     size_t        instanceStride() const { return fInstanceStride; }
129 
numUniforms()130     size_t numUniforms()           const { return fUniforms.size();      }
numVertexAttributes()131     size_t numVertexAttributes()   const { return fVertexAttrs.size();   }
numInstanceAttributes()132     size_t numInstanceAttributes() const { return fInstanceAttrs.size(); }
133 
134     // Name of an attribute containing both render step and shading SSBO indices, if used.
ssboIndicesAttribute()135     static const char* ssboIndicesAttribute() { return "ssboIndices"; }
136 
137     // Name of a varying to pass SSBO indices to fragment shader. Both render step and shading
138     // indices are passed, because render step uniforms are sometimes used for coverage.
ssboIndicesVarying()139     static const char* ssboIndicesVarying() { return "ssboIndicesVar"; }
140 
141     // The uniforms of a RenderStep are bound to the kRenderStep slot, the rest of the pipeline
142     // may still use uniforms bound to other slots.
uniforms()143     SkSpan<const Uniform> uniforms()             const { return SkSpan(fUniforms);      }
vertexAttributes()144     SkSpan<const Attribute> vertexAttributes()   const { return SkSpan(fVertexAttrs);   }
instanceAttributes()145     SkSpan<const Attribute> instanceAttributes() const { return SkSpan(fInstanceAttrs); }
varyings()146     SkSpan<const Varying>   varyings()           const { return SkSpan(fVaryings);      }
147 
depthStencilSettings()148     const DepthStencilSettings& depthStencilSettings() const { return fDepthStencilSettings; }
149 
depthStencilFlags()150     SkEnumBitMask<DepthStencilFlags> depthStencilFlags() const {
151         return (fDepthStencilSettings.fStencilTestEnabled
152                         ? DepthStencilFlags::kStencil : DepthStencilFlags::kNone) |
153                (fDepthStencilSettings.fDepthTestEnabled || fDepthStencilSettings.fDepthWriteEnabled
154                         ? DepthStencilFlags::kDepth : DepthStencilFlags::kNone);
155     }
156 
157     // TODO: Actual API to do things
158     // 6. Some Renderers benefit from being able to share vertices between RenderSteps. Must find a
159     //    way to support that. It may mean that RenderSteps get state per draw.
160     //    - Does Renderer make RenderStepFactories that create steps for each DrawList::Draw?
161     //    - Does DrawList->DrawPass conversion build a separate array of blind data that the
162     //      stateless Renderstep can refer to for {draw,step} pairs?
163     //    - Does each DrawList::Draw have extra space (e.g. 8 bytes) that steps can cache data in?
164 protected:
165     enum class Flags : unsigned {
166         kNone                  = 0b0000000,
167         kRequiresMSAA          = 0b0000001,
168         kPerformsShading       = 0b0000010,
169         kHasTextures           = 0b0000100,
170         kEmitsCoverage         = 0b0001000,
171         kLCDCoverage           = 0b0010000,
172         kEmitsPrimitiveColor   = 0b0100000,
173         kOutsetBoundsForAA     = 0b1000000,
174     };
175     SK_DECL_BITMASK_OPS_FRIENDS(Flags)
176 
177     // While RenderStep does not define the full program that's run for a draw, it defines the
178     // entire vertex layout of the pipeline. This is not allowed to change, so can be provided to
179     // the RenderStep constructor by subclasses.
180     RenderStep(std::string_view className,
181                std::string_view variantName,
182                SkEnumBitMask<Flags> flags,
183                std::initializer_list<Uniform> uniforms,
184                PrimitiveType primitiveType,
185                DepthStencilSettings depthStencilSettings,
186                SkSpan<const Attribute> vertexAttrs,
187                SkSpan<const Attribute> instanceAttrs,
188                SkSpan<const Varying> varyings = {});
189 
190 private:
191     friend class Renderer; // for Flags
192 
193     // Cannot copy or move
194     RenderStep(const RenderStep&) = delete;
195     RenderStep(RenderStep&&)      = delete;
196 
197     static Coverage GetCoverage(SkEnumBitMask<Flags>);
198 
199     uint32_t fUniqueID;
200     SkEnumBitMask<Flags> fFlags;
201     PrimitiveType        fPrimitiveType;
202 
203     DepthStencilSettings fDepthStencilSettings;
204 
205     // TODO: When we always use C++17 for builds, we should be able to just let subclasses declare
206     // constexpr arrays and point to those, but we need explicit storage for C++14.
207     // Alternatively, if we imposed a max attr count, similar to Renderer's num render steps, we
208     // could just have this be std::array and keep all attributes inline with the RenderStep memory.
209     // On the other hand, the attributes are only needed when creating a new pipeline so it's not
210     // that performance sensitive.
211     std::vector<Uniform>   fUniforms;
212     std::vector<Attribute> fVertexAttrs;
213     std::vector<Attribute> fInstanceAttrs;
214     std::vector<Varying>   fVaryings;
215 
216     size_t fVertexStride;   // derived from vertex attribute set
217     size_t fInstanceStride; // derived from instance attribute set
218 
219     std::string fName;
220 };
SK_MAKE_BITMASK_OPS(RenderStep::Flags)221 SK_MAKE_BITMASK_OPS(RenderStep::Flags)
222 
223 class Renderer {
224     using StepFlags = RenderStep::Flags;
225 public:
226     // The maximum number of render steps that any Renderer is allowed to have.
227     static constexpr int kMaxRenderSteps = 4;
228 
229     const RenderStep& step(int i) const {
230         SkASSERT(i >= 0 && i < fStepCount);
231         return *fSteps[i];
232     }
233     SkSpan<const RenderStep* const> steps() const {
234         SkASSERT(fStepCount > 0); // steps() should only be called on valid Renderers.
235         return {fSteps.data(), static_cast<size_t>(fStepCount) };
236     }
237 
238     const char*   name()           const { return fName.c_str(); }
239     DrawTypeFlags drawTypes()      const { return fDrawTypes; }
240     int           numRenderSteps() const { return fStepCount;    }
241 
242     bool requiresMSAA() const {
243         return SkToBool(fStepFlags & StepFlags::kRequiresMSAA);
244     }
245     bool emitsPrimitiveColor() const {
246         return SkToBool(fStepFlags & StepFlags::kEmitsPrimitiveColor);
247     }
248     bool outsetBoundsForAA() const {
249         return SkToBool(fStepFlags & StepFlags::kOutsetBoundsForAA);
250     }
251 
252     SkEnumBitMask<DepthStencilFlags> depthStencilFlags() const { return fDepthStencilFlags; }
253 
254     Coverage coverage() const { return RenderStep::GetCoverage(fStepFlags); }
255 
256 private:
257     friend class RendererProvider; // for ctors
258 
259     // Max render steps is 4, so just spell the options out for now...
260     Renderer(std::string_view name, DrawTypeFlags drawTypes, const RenderStep* s1)
261             : Renderer(name, drawTypes, std::array<const RenderStep*, 1>{s1}) {}
262 
263     Renderer(std::string_view name, DrawTypeFlags drawTypes,
264              const RenderStep* s1, const RenderStep* s2)
265             : Renderer(name, drawTypes, std::array<const RenderStep*, 2>{s1, s2}) {}
266 
267     Renderer(std::string_view name, DrawTypeFlags drawTypes,
268              const RenderStep* s1, const RenderStep* s2, const RenderStep* s3)
269             : Renderer(name, drawTypes, std::array<const RenderStep*, 3>{s1, s2, s3}) {}
270 
271     Renderer(std::string_view name, DrawTypeFlags drawTypes,
272              const RenderStep* s1, const RenderStep* s2, const RenderStep* s3, const RenderStep* s4)
273             : Renderer(name, drawTypes, std::array<const RenderStep*, 4>{s1, s2, s3, s4}) {}
274 
275     template<size_t N>
276     Renderer(std::string_view name, DrawTypeFlags drawTypes, std::array<const RenderStep*, N> steps)
277             : fName(name)
278             , fDrawTypes(drawTypes)
279             , fStepCount(SkTo<int>(N)) {
280         static_assert(N <= kMaxRenderSteps);
281         for (int i = 0 ; i < fStepCount; ++i) {
282             fSteps[i] = steps[i];
283             fStepFlags |= fSteps[i]->fFlags;
284             fDepthStencilFlags |= fSteps[i]->depthStencilFlags();
285         }
286         // At least one step needs to actually shade.
287         SkASSERT(fStepFlags & RenderStep::Flags::kPerformsShading);
288     }
289 
290     // For RendererProvider to manage initialization; it will never expose a Renderer that is only
291     // default-initialized and not replaced because it's algorithm is disabled by caps/options.
292     Renderer() : fSteps(), fName(""), fStepCount(0) {}
293     Renderer& operator=(Renderer&&) = default;
294 
295     std::array<const RenderStep*, kMaxRenderSteps> fSteps;
296     std::string fName;
297     DrawTypeFlags fDrawTypes = DrawTypeFlags::kNone;
298     int fStepCount;
299 
300     SkEnumBitMask<StepFlags> fStepFlags = StepFlags::kNone;
301     SkEnumBitMask<DepthStencilFlags> fDepthStencilFlags = DepthStencilFlags::kNone;
302 };
303 
304 } // namespace skgpu::graphite
305 
306 #endif // skgpu_graphite_Renderer_DEFINED
307