/src/skia/src/gpu/graphite/Renderer.h
Line | Count | Source (jump to first uncovered line) |
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 | 0 | 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. |
96 | | virtual std::string texturesAndSamplersSkSL(const ResourceBindingRequirements&, |
97 | 0 | int* nextBindingIndex) const { |
98 | 0 | return R"()"; |
99 | 0 | } |
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. |
105 | 0 | 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). |
110 | 0 | virtual const char* fragmentColorSkSL() const { return R"()"; } |
111 | | |
112 | 0 | 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. |
116 | 0 | const char* name() const { return fName.c_str(); } |
117 | | |
118 | 0 | bool requiresMSAA() const { return SkToBool(fFlags & Flags::kRequiresMSAA); } |
119 | 0 | bool performsShading() const { return SkToBool(fFlags & Flags::kPerformsShading); } |
120 | 0 | bool hasTextures() const { return SkToBool(fFlags & Flags::kHasTextures); } |
121 | 0 | bool emitsPrimitiveColor() const { return SkToBool(fFlags & Flags::kEmitsPrimitiveColor); } |
122 | 0 | bool outsetBoundsForAA() const { return SkToBool(fFlags & Flags::kOutsetBoundsForAA); } |
123 | | |
124 | 0 | Coverage coverage() const { return RenderStep::GetCoverage(fFlags); } |
125 | | |
126 | 0 | PrimitiveType primitiveType() const { return fPrimitiveType; } |
127 | 0 | size_t vertexStride() const { return fVertexStride; } |
128 | 0 | size_t instanceStride() const { return fInstanceStride; } |
129 | | |
130 | 0 | size_t numUniforms() const { return fUniforms.size(); } |
131 | 0 | size_t numVertexAttributes() const { return fVertexAttrs.size(); } |
132 | 0 | size_t numInstanceAttributes() const { return fInstanceAttrs.size(); } |
133 | | |
134 | | // Name of an attribute containing both render step and shading SSBO indices, if used. |
135 | 0 | 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. |
139 | 0 | 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. |
143 | 0 | SkSpan<const Uniform> uniforms() const { return SkSpan(fUniforms); } |
144 | 0 | SkSpan<const Attribute> vertexAttributes() const { return SkSpan(fVertexAttrs); } |
145 | 0 | SkSpan<const Attribute> instanceAttributes() const { return SkSpan(fInstanceAttrs); } |
146 | 0 | SkSpan<const Varying> varyings() const { return SkSpan(fVaryings); } |
147 | | |
148 | 0 | const DepthStencilSettings& depthStencilSettings() const { return fDepthStencilSettings; } |
149 | | |
150 | 0 | SkEnumBitMask<DepthStencilFlags> depthStencilFlags() const { |
151 | 0 | return (fDepthStencilSettings.fStencilTestEnabled |
152 | 0 | ? DepthStencilFlags::kStencil : DepthStencilFlags::kNone) | |
153 | 0 | (fDepthStencilSettings.fDepthTestEnabled || fDepthStencilSettings.fDepthWriteEnabled |
154 | 0 | ? DepthStencilFlags::kDepth : DepthStencilFlags::kNone); |
155 | 0 | } |
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 | | }; |
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 | 0 | const RenderStep& step(int i) const { |
230 | 0 | SkASSERT(i >= 0 && i < fStepCount); |
231 | 0 | return *fSteps[i]; |
232 | 0 | } Unexecuted instantiation: skgpu::graphite::Renderer::step(int) const Unexecuted instantiation: skgpu::graphite::Renderer::step(int) const |
233 | 0 | SkSpan<const RenderStep* const> steps() const { |
234 | 0 | SkASSERT(fStepCount > 0); // steps() should only be called on valid Renderers. |
235 | 0 | return {fSteps.data(), static_cast<size_t>(fStepCount) }; |
236 | 0 | } Unexecuted instantiation: skgpu::graphite::Renderer::steps() const Unexecuted instantiation: skgpu::graphite::Renderer::steps() const |
237 | | |
238 | 0 | const char* name() const { return fName.c_str(); } |
239 | 0 | DrawTypeFlags drawTypes() const { return fDrawTypes; } |
240 | 0 | int numRenderSteps() const { return fStepCount; } |
241 | | |
242 | 0 | bool requiresMSAA() const { |
243 | 0 | return SkToBool(fStepFlags & StepFlags::kRequiresMSAA); |
244 | 0 | } |
245 | 0 | bool emitsPrimitiveColor() const { |
246 | 0 | return SkToBool(fStepFlags & StepFlags::kEmitsPrimitiveColor); |
247 | 0 | } |
248 | 0 | bool outsetBoundsForAA() const { |
249 | 0 | return SkToBool(fStepFlags & StepFlags::kOutsetBoundsForAA); |
250 | 0 | } |
251 | | |
252 | 0 | SkEnumBitMask<DepthStencilFlags> depthStencilFlags() const { return fDepthStencilFlags; } |
253 | | |
254 | 0 | 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 | 0 | : 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 | 0 | : 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 | 0 | : 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 | 0 | : 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 | 0 | , fStepCount(SkTo<int>(N)) { |
280 | 0 | static_assert(N <= kMaxRenderSteps); |
281 | 0 | for (int i = 0 ; i < fStepCount; ++i) { |
282 | 0 | fSteps[i] = steps[i]; |
283 | 0 | fStepFlags |= fSteps[i]->fFlags; |
284 | 0 | fDepthStencilFlags |= fSteps[i]->depthStencilFlags(); |
285 | 0 | } |
286 | | // At least one step needs to actually shade. |
287 | 0 | SkASSERT(fStepFlags & RenderStep::Flags::kPerformsShading); |
288 | 0 | } Unexecuted instantiation: skgpu::graphite::Renderer::Renderer<1ul>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, skgpu::graphite::DrawTypeFlags, std::__1::array<skgpu::graphite::RenderStep const*, 1ul>) Unexecuted instantiation: skgpu::graphite::Renderer::Renderer<3ul>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, skgpu::graphite::DrawTypeFlags, std::__1::array<skgpu::graphite::RenderStep const*, 3ul>) Unexecuted instantiation: skgpu::graphite::Renderer::Renderer<2ul>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, skgpu::graphite::DrawTypeFlags, std::__1::array<skgpu::graphite::RenderStep const*, 2ul>) Unexecuted instantiation: skgpu::graphite::Renderer::Renderer<1ul>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, skgpu::graphite::DrawTypeFlags, std::__1::array<skgpu::graphite::RenderStep const*, 1ul>) Unexecuted instantiation: skgpu::graphite::Renderer::Renderer<3ul>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, skgpu::graphite::DrawTypeFlags, std::__1::array<skgpu::graphite::RenderStep const*, 3ul>) Unexecuted instantiation: skgpu::graphite::Renderer::Renderer<2ul>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, skgpu::graphite::DrawTypeFlags, std::__1::array<skgpu::graphite::RenderStep const*, 2ul>) |
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 | 0 | Renderer() : fSteps(), fName(""), fStepCount(0) {} |
293 | 0 | 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 |