• 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_Renderer_DEFINED
9 #define skgpu_Renderer_DEFINED
10 
11 #include "experimental/graphite/src/Attribute.h"
12 #include "experimental/graphite/src/DrawTypes.h"
13 #include "experimental/graphite/src/EnumBitMask.h"
14 #include "experimental/graphite/src/ResourceTypes.h"
15 
16 #include "include/core/SkSpan.h"
17 #include "include/core/SkString.h"
18 #include "include/core/SkTypes.h"
19 #include "src/core/SkUniform.h"
20 
21 #include <array>
22 #include <initializer_list>
23 #include <vector>
24 
25 struct SkIRect;
26 enum class SkPathFillType;
27 class SkUniformData;
28 
29 namespace skgpu {
30 
31 class DrawWriter;
32 class ResourceProvider;
33 class Shape;
34 class Transform;
35 
36 enum class Layout;
37 
38 class RenderStep {
39 public:
40     virtual ~RenderStep() = default;
41 
42     // The DrawWriter is configured with the vertex and instance strides of the RenderStep, and its
43     // primitive type. The recorded draws will be executed with a graphics pipeline compatible with
44     // this RenderStep.
45     virtual void writeVertices(DrawWriter*,
46                                const SkIRect& bounds,
47                                const Transform&,
48                                const Shape&) const = 0;
49 
50     // Write out the uniform values (aligned for the layout). These values will be de-duplicated
51     // across all draws using the RenderStep before uploading to the GPU, but it can be assumed the
52     // uniforms will be bound before the draws recorded in 'writeVertices' are executed.
53     // TODO: We definitely want this to return CPU memory since it's better for the caller to handle
54     // the de-duplication and GPU upload/binding (DrawPass tracks all this). However, a RenderStep's
55     // uniforms aren't going to change, and the Layout won't change during a process, so it would be
56     // nice if we could remember the offsets for the layout/gpu and reuse them across draws.
57     // Similarly, it would be nice if this could write into reusable storage and then DrawPass or
58     // UniformCache handles making an sk_sp if we need to assign a new unique ID to the uniform data
59     virtual sk_sp<SkUniformData> writeUniforms(Layout layout,
60                                                const SkIRect& bounds,
61                                                const Transform&,
62                                                const Shape&) const = 0;
63 
64     virtual const char* name()      const = 0;
65 
66     // TODO: This is only temporary. Eventually the RenderStep will define its logic in SkSL and
67     // be able to have code operate in both the vertex and fragment shaders. Ideally the RenderStep
68     // will provide two functions that fit some ABI for integrating with the common and paint SkSL,
69     // although we could go as far as allowing RenderStep to handle composing the final SkSL if
70     // given the paint combination's SkSL.
71 
72     // Returns the body of a vertex function, which must define a float4 devPosition variable.
73     // It has access to the variables declared by vertexAttributes(), instanceAttributes(),
74     // and uniforms().
75     //
76     // NOTE: The above contract is mainly so that the entire SkSL program can be created by just str
77     // concatenating struct definitions generated from the RenderStep and paint Combination
78     // and then including the function bodies returned here.
79     virtual const char* vertexSkSL() const = 0;
80 
requiresMSAA()81     bool          requiresMSAA()    const { return fFlags & Flags::kRequiresMSAA;    }
performsShading()82     bool          performsShading() const { return fFlags & Flags::kPerformsShading; }
83 
primitiveType()84     PrimitiveType primitiveType()   const { return fPrimitiveType;  }
vertexStride()85     size_t        vertexStride()    const { return fVertexStride;   }
instanceStride()86     size_t        instanceStride()  const { return fInstanceStride; }
87 
depthStencilSettings()88     const DepthStencilSettings& depthStencilSettings() const { return fDepthStencilSettings; }
89 
depthStencilFlags()90     Mask<DepthStencilFlags> depthStencilFlags() const {
91         return (fDepthStencilSettings.fStencilTestEnabled
92                         ? DepthStencilFlags::kStencil : DepthStencilFlags::kNone) |
93                (fDepthStencilSettings.fDepthTestEnabled || fDepthStencilSettings.fDepthWriteEnabled
94                         ? DepthStencilFlags::kDepth : DepthStencilFlags::kNone);
95     }
96 
numUniforms()97     size_t numUniforms()            const { return fUniforms.size();      }
numVertexAttributes()98     size_t numVertexAttributes()    const { return fVertexAttrs.size();   }
numInstanceAttributes()99     size_t numInstanceAttributes()  const { return fInstanceAttrs.size(); }
100 
101     // The uniforms of a RenderStep are bound to the kRenderStep slot, the rest of the pipeline
102     // may still use uniforms bound to other slots.
uniforms()103     SkSpan<const SkUniform> uniforms()           const { return SkMakeSpan(fUniforms);      }
vertexAttributes()104     SkSpan<const Attribute> vertexAttributes()   const { return SkMakeSpan(fVertexAttrs);   }
instanceAttributes()105     SkSpan<const Attribute> instanceAttributes() const { return SkMakeSpan(fInstanceAttrs); }
106 
107 
108     // TODO: Actual API to do things
109     // 1. Provide stencil settings
110     // 6. Some Renderers benefit from being able to share vertices between RenderSteps. Must find a
111     //    way to support that. It may mean that RenderSteps get state per draw.
112     //    - Does Renderer make RenderStepFactories that create steps for each DrawList::Draw?
113     //    - Does DrawList->DrawPass conversion build a separate array of blind data that the
114     //      stateless Renderstep can refer to for {draw,step} pairs?
115     //    - Does each DrawList::Draw have extra space (e.g. 8 bytes) that steps can cache data in?
116 protected:
117     enum class Flags : unsigned {
118         kNone            = 0b000,
119         kRequiresMSAA    = 0b001,
120         kPerformsShading = 0b010,
121     };
122     SKGPU_DECL_MASK_OPS_FRIENDS(Flags);
123 
124     // While RenderStep does not define the full program that's run for a draw, it defines the
125     // entire vertex layout of the pipeline. This is not allowed to change, so can be provided to
126     // the RenderStep constructor by subclasses.
RenderStep(Mask<Flags> flags,std::initializer_list<SkUniform> uniforms,PrimitiveType primitiveType,DepthStencilSettings depthStencilSettings,std::initializer_list<Attribute> vertexAttrs,std::initializer_list<Attribute> instanceAttrs)127     RenderStep(Mask<Flags> flags,
128                std::initializer_list<SkUniform> uniforms,
129                PrimitiveType primitiveType,
130                DepthStencilSettings depthStencilSettings,
131                std::initializer_list<Attribute> vertexAttrs,
132                std::initializer_list<Attribute> instanceAttrs)
133             : fFlags(flags)
134             , fPrimitiveType(primitiveType)
135             , fDepthStencilSettings(depthStencilSettings)
136             , fUniforms(uniforms)
137             , fVertexAttrs(vertexAttrs)
138             , fInstanceAttrs(instanceAttrs)
139             , fVertexStride(0)
140             , fInstanceStride(0) {
141         for (auto v : this->vertexAttributes()) {
142             fVertexStride += v.sizeAlign4();
143         }
144         for (auto i : this->instanceAttributes()) {
145             fInstanceStride += i.sizeAlign4();
146         }
147     }
148 
149 private:
150     // Cannot copy or move
151     RenderStep(const RenderStep&) = delete;
152     RenderStep(RenderStep&&)      = delete;
153 
154     Mask<Flags>   fFlags;
155     PrimitiveType fPrimitiveType;
156 
157     DepthStencilSettings fDepthStencilSettings;
158 
159     // TODO: When we always use C++17 for builds, we should be able to just let subclasses declare
160     // constexpr arrays and point to those, but we need explicit storage for C++14.
161     // Alternatively, if we imposed a max attr count, similar to Renderer's num render steps, we
162     // could just have this be std::array and keep all attributes inline with the RenderStep memory.
163     // On the other hand, the attributes are only needed when creating a new pipeline so it's not
164     // that performance sensitive.
165     std::vector<SkUniform> fUniforms;
166     std::vector<Attribute> fVertexAttrs;
167     std::vector<Attribute> fInstanceAttrs;
168 
169     size_t fVertexStride;   // derived from vertex attribute set
170     size_t fInstanceStride; // derived from instance attribute set
171 };
172 SKGPU_MAKE_MASK_OPS(RenderStep::Flags);
173 
174 /**
175  * The actual technique for rasterizing a high-level draw recorded in a DrawList is handled by a
176  * specific Renderer. Each technique has an associated singleton Renderer that decomposes the
177  * technique into a series of RenderSteps that must be executed in the specified order for the draw.
178  * However, the RenderStep executions for multiple draws can be re-arranged so batches of each
179  * step can be performed in a larger GPU operation. This re-arranging relies on accurate
180  * determination of the DisjointStencilIndex for each draw so that stencil steps are not corrupted
181  * by another draw before its cover step is executed. It also relies on the CompressedPaintersOrder
182  * for each draw to ensure steps are not re-arranged in a way that violates the original draw order.
183  *
184  * Renderer itself is non-virtual since it simply has to point to a list of RenderSteps. RenderSteps
185  * on the other hand are virtual implement the technique specific functionality. It is entirely
186  * possible for certain types of steps, e.g. a bounding box cover, to be re-used across different
187  * Renderers even if the preceeding steps were different.
188  */
189 class Renderer {
190 public:
191     // Graphite defines a limited set of renderers in order to increase likelihood of batching
192     // across draw calls, and reduce the number of shader permutations required. These Renderers
193     // are stateless singletons and remain alive for the entire program. Each Renderer corresponds
194     // to a specific recording function on DrawList and fill type.
195     static const Renderer& StencilAndFillPath(SkPathFillType);
196     // TODO: Not on the immediate sprint target, but show what needs to be added for DrawList's API
197     // static const Renderer& FillConvexPath();
198     // static const Renderer& StrokePath();
199     // TODO: Will add more of these as primitive rendering etc. is fleshed out
200 
201     // The maximum number of render steps that any Renderer is allowed to have.
202     static constexpr int kMaxRenderSteps = 4;
203 
steps()204     SkSpan<const RenderStep* const> steps() const {
205         return {&fSteps.front(), static_cast<size_t>(fStepCount) };
206     }
207 
name()208     const char* name()            const { return fName.c_str();    }
numRenderSteps()209     int         numRenderSteps()  const { return fStepCount;       }
requiresMSAA()210     bool        requiresMSAA()    const { return fRequiresMSAA;    }
211 
depthStencilFlags()212     Mask<DepthStencilFlags> depthStencilFlags() const { return fDepthStencilFlags; }
213 
214 private:
215     // max render steps is 4, so just spell the options out for now...
Renderer(const char * name,const RenderStep * s1)216     Renderer(const char* name, const RenderStep* s1)
217             : Renderer(name, std::array<const RenderStep*, 1>{s1}) {}
218 
Renderer(const char * name,const RenderStep * s1,const RenderStep * s2)219     Renderer(const char* name, const RenderStep* s1, const RenderStep* s2)
220             : Renderer(name, std::array<const RenderStep*, 2>{s1, s2}) {}
221 
Renderer(const char * name,const RenderStep * s1,const RenderStep * s2,const RenderStep * s3)222     Renderer(const char* name, const RenderStep* s1, const RenderStep* s2, const RenderStep* s3)
223             : Renderer(name, std::array<const RenderStep*, 3>{s1, s2, s3}) {}
224 
Renderer(const char * name,const RenderStep * s1,const RenderStep * s2,const RenderStep * s3,const RenderStep * s4)225     Renderer(const char* name, const RenderStep* s1, const RenderStep* s2,
226              const RenderStep* s3, const RenderStep* s4)
227             : Renderer(name, std::array<const RenderStep*, 4>{s1, s2, s3, s4}) {}
228 
229     template<size_t N>
Renderer(const char * name,std::array<const RenderStep *,N> steps)230     Renderer(const char* name, std::array<const RenderStep*, N> steps)
231             : fName(name)
232             , fStepCount(SkTo<int>(N)) {
233         static_assert(N <= kMaxRenderSteps);
234         SkDEBUGCODE(bool performsShading = false;)
235         for (int i = 0 ; i < fStepCount; ++i) {
236             fSteps[i] = steps[i];
237             fDepthStencilFlags |= fSteps[i]->depthStencilFlags();
238             SkDEBUGCODE(performsShading |= fSteps[i]->performsShading());
239         }
240         SkASSERT(performsShading); // at least one step needs to actually shade
241     }
242 
243     // Cannot move or copy
244     Renderer(const Renderer&) = delete;
245     Renderer(Renderer&&)      = delete;
246 
247     std::array<const RenderStep*, kMaxRenderSteps> fSteps;
248 
249     SkString fName;
250     int      fStepCount;
251     bool     fRequiresMSAA = false;
252 
253     Mask<DepthStencilFlags> fDepthStencilFlags = DepthStencilFlags::kNone;
254 };
255 
256 } // skgpu namespace
257 
258 #endif // skgpu_Renderer_DEFINED
259