xref: /aosp_15_r20/external/skia/src/gpu/ganesh/GrGeometryProcessor.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2013 Google Inc.
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 GrGeometryProcessor_DEFINED
9 #define GrGeometryProcessor_DEFINED
10 
11 #include "include/core/SkMatrix.h"
12 #include "include/gpu/ganesh/GrBackendSurface.h"
13 #include "include/private/base/SkAlign.h"
14 #include "include/private/base/SkAssert.h"
15 #include "include/private/base/SkTo.h"
16 #include "include/private/gpu/ganesh/GrTypesPriv.h"
17 #include "src/core/SkSLTypeShared.h"
18 #include "src/gpu/Swizzle.h"
19 #include "src/gpu/ganesh/GrFragmentProcessor.h"
20 #include "src/gpu/ganesh/GrProcessor.h"
21 #include "src/gpu/ganesh/GrSamplerState.h"
22 #include "src/gpu/ganesh/GrShaderCaps.h"
23 #include "src/gpu/ganesh/GrShaderVar.h"
24 #include "src/gpu/ganesh/glsl/GrGLSLProgramDataManager.h"
25 #include "src/gpu/ganesh/glsl/GrGLSLUniformHandler.h"
26 #include "src/gpu/ganesh/glsl/GrGLSLVarying.h"
27 
28 #include <cstddef>
29 #include <cstdint>
30 #include <memory>
31 #include <optional>
32 #include <tuple>
33 #include <unordered_map>
34 
35 class GrGLSLFPFragmentBuilder;
36 class GrGLSLVertexBuilder;
37 class GrPipeline;
38 namespace skgpu {
39 class KeyBuilder;
40 }
41 
42 /**
43  * The GrGeometryProcessor represents some kind of geometric primitive.  This includes the shape
44  * of the primitive and the inherent color of the primitive.  The GrGeometryProcessor is
45  * responsible for providing a color and coverage input into the Ganesh rendering pipeline. Through
46  * optimization, Ganesh may decide a different color, no color, and / or no coverage are required
47  * from the GrGeometryProcessor, so the GrGeometryProcessor must be able to support this
48  * functionality.
49  *
50  * There are two feedback loops between the GrFragmentProcessors, the GrXferProcessor, and the
51  * GrGeometryProcessor. These loops run on the CPU and to determine known properties of the final
52  * color and coverage inputs to the GrXferProcessor in order to perform optimizations that preserve
53  * correctness. The GrDrawOp seeds these loops with initial color and coverage, in its
54  * getProcessorAnalysisInputs implementation. These seed values are processed by the
55  * subsequent stages of the rendering pipeline and the output is then fed back into the GrDrawOp
56  * in the applyPipelineOptimizations call, where the op can use the information to inform
57  * decisions about GrGeometryProcessor creation.
58  *
59  * Note that all derived classes should hide their constructors and provide a Make factory
60  * function that takes an arena (except for Tesselation-specific classes). This is because
61  * geometry processors can be created in either the record-time or flush-time arenas which
62  * define their lifetimes (i.e., a DDLs life time in the first case and a single flush in
63  * the second case).
64  */
65 class GrGeometryProcessor : public GrProcessor {
66 public:
67     /**
68      * Every GrGeometryProcessor must be capable of creating a subclass of ProgramImpl. The
69      * ProgramImpl emits the shader code that implements the GrGeometryProcessor, is attached to the
70      * generated backend API pipeline/program and used to extract uniform data from
71      * GrGeometryProcessor instances.
72      */
73     class ProgramImpl;
74 
75     class TextureSampler;
76 
77     /** Describes a vertex or instance attribute. */
78     class Attribute {
79     public:
AlignOffset(size_t offset)80         static constexpr size_t AlignOffset(size_t offset) { return SkAlign4(offset); }
81 
82         constexpr Attribute() = default;
83         /**
84          * Makes an attribute whose offset will be implicitly determined by the types and ordering
85          * of an array attributes.
86          */
Attribute(const char * name,GrVertexAttribType cpuType,SkSLType gpuType)87         constexpr Attribute(const char* name,
88                             GrVertexAttribType cpuType,
89                             SkSLType gpuType)
90                 : fName(name), fCPUType(cpuType), fGPUType(gpuType) {
91             SkASSERT(name && gpuType != SkSLType::kVoid);
92         }
93         /**
94          * Makes an attribute with an explicit offset.
95          */
Attribute(const char * name,GrVertexAttribType cpuType,SkSLType gpuType,size_t offset)96         constexpr Attribute(const char*        name,
97                             GrVertexAttribType cpuType,
98                             SkSLType           gpuType,
99                             size_t             offset)
100                 : fName(name), fCPUType(cpuType), fGPUType(gpuType), fOffset(SkToU32(offset)) {
101             SkASSERT(AlignOffset(offset) == offset);
102             SkASSERT(name && gpuType != SkSLType::kVoid);
103         }
104         constexpr Attribute(const Attribute&) = default;
105 
106         Attribute& operator=(const Attribute&) = default;
107 
isInitialized()108         constexpr bool isInitialized() const { return fGPUType != SkSLType::kVoid; }
109 
name()110         constexpr const char*           name() const { return fName; }
cpuType()111         constexpr GrVertexAttribType cpuType() const { return fCPUType; }
gpuType()112         constexpr SkSLType           gpuType() const { return fGPUType; }
113         /**
114          * Returns the offset if attributes were specified with explicit offsets. Otherwise,
115          * offsets (and total vertex stride) are implicitly determined from attribute order and
116          * types.
117          */
offset()118         std::optional<size_t> offset() const {
119             if (fOffset != kImplicitOffset) {
120                 SkASSERT(AlignOffset(fOffset) == fOffset);
121                 return {fOffset};
122             }
123             return std::nullopt;
124         }
125 
126         inline constexpr size_t size() const;
127 
asShaderVar()128         GrShaderVar asShaderVar() const {
129             return {fName, fGPUType, GrShaderVar::TypeModifier::In};
130         }
131 
132     private:
133         static constexpr uint32_t kImplicitOffset = 1;  // 1 is not valid because it isn't aligned.
134 
135         const char*        fName    = nullptr;
136         GrVertexAttribType fCPUType = kFloat_GrVertexAttribType;
137         SkSLType           fGPUType = SkSLType::kVoid;
138         uint32_t           fOffset  = kImplicitOffset;
139     };
140 
141     /**
142      * A set of attributes that can iterated. The iterator handles hides two pieces of complexity:
143      * 1) It skips uninitialized attributes.
144      * 2) It always returns an attribute with a known offset.
145      */
146     class AttributeSet {
147         class Iter {
148         public:
149             Iter() = default;
150             Iter(const Iter& iter) = default;
151             Iter& operator=(const Iter& iter) = default;
152 
Iter(const Attribute * attrs,int count)153             Iter(const Attribute* attrs, int count) : fCurr(attrs), fRemaining(count) {
154                 this->skipUninitialized();
155             }
156 
157             bool operator!=(const Iter& that) const { return fCurr != that.fCurr; }
158             Attribute operator*() const;
159             void operator++();
160 
161         private:
162             void skipUninitialized();
163 
164             const Attribute* fCurr           = nullptr;
165             int              fRemaining      = 0;
166             size_t           fImplicitOffset = 0;
167         };
168 
169     public:
170         Iter begin() const;
171         Iter end() const;
172 
count()173         int count() const { return fCount; }
stride()174         size_t stride() const { return fStride; }
175 
176         // Init with implicit offsets and stride. No attributes can have a predetermined stride.
177         void initImplicit(const Attribute* attrs, int count);
178         // Init with explicit offsets and stride. All attributes must be initialized and have
179         // an explicit offset aligned to 4 bytes and with no attribute crossing stride boundaries.
180         void initExplicit(const Attribute* attrs, int count, size_t stride);
181 
182         void addToKey(skgpu::KeyBuilder* b) const;
183 
184     private:
185         const Attribute* fAttributes = nullptr;
186         int              fRawCount = 0;
187         int              fCount = 0;
188         size_t           fStride = 0;
189     };
190 
191     GrGeometryProcessor(ClassID);
192 
numTextureSamplers()193     int numTextureSamplers() const { return fTextureSamplerCnt; }
194     const TextureSampler& textureSampler(int index) const;
numVertexAttributes()195     int numVertexAttributes() const { return fVertexAttributes.count(); }
vertexAttributes()196     const AttributeSet& vertexAttributes() const { return fVertexAttributes; }
numInstanceAttributes()197     int numInstanceAttributes() const { return fInstanceAttributes.count(); }
instanceAttributes()198     const AttributeSet& instanceAttributes() const { return fInstanceAttributes; }
199 
hasVertexAttributes()200     bool hasVertexAttributes() const { return SkToBool(fVertexAttributes.count()); }
hasInstanceAttributes()201     bool hasInstanceAttributes() const { return SkToBool(fInstanceAttributes.count()); }
202 
203     /**
204      * A common practice is to populate the the vertex/instance's memory using an implicit array of
205      * structs. In this case, it is best to assert that:
206      *     stride == sizeof(struct)
207      */
vertexStride()208     size_t vertexStride() const { return fVertexAttributes.stride(); }
instanceStride()209     size_t instanceStride() const { return fInstanceAttributes.stride(); }
210 
211     /**
212      * Computes a key for the transforms owned by an FP based on the shader code that will be
213      * emitted by the primitive processor to implement them.
214      */
215     static uint32_t ComputeCoordTransformsKey(const GrFragmentProcessor& fp);
216 
217     inline static constexpr int kCoordTransformKeyBits = 4;
218 
219     /**
220      * Adds a key on the skgpu::KeyBuilder that reflects any variety in the code that the
221      * geometry processor subclass can emit.
222      */
223     virtual void addToKey(const GrShaderCaps&, skgpu::KeyBuilder*) const = 0;
224 
225     void getAttributeKey(skgpu::KeyBuilder* b) const;
226 
227     /**
228      * Returns a new instance of the appropriate implementation class for the given
229      * GrGeometryProcessor.
230      */
231     virtual std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const = 0;
232 
233 protected:
234     // GPs that need to use either float or ubyte colors can just call this to get a correctly
235     // configured Attribute struct
MakeColorAttribute(const char * name,bool wideColor)236     static Attribute MakeColorAttribute(const char* name, bool wideColor) {
237         return { name,
238                  wideColor ? kFloat4_GrVertexAttribType : kUByte4_norm_GrVertexAttribType,
239                  SkSLType::kHalf4 };
240     }
setVertexAttributes(const Attribute * attrs,int attrCount,size_t stride)241     void setVertexAttributes(const Attribute* attrs, int attrCount, size_t stride) {
242         fVertexAttributes.initExplicit(attrs, attrCount, stride);
243     }
setInstanceAttributes(const Attribute * attrs,int attrCount,size_t stride)244     void setInstanceAttributes(const Attribute* attrs, int attrCount, size_t stride) {
245         SkASSERT(attrCount >= 0);
246         fInstanceAttributes.initExplicit(attrs, attrCount, stride);
247     }
248 
setVertexAttributesWithImplicitOffsets(const Attribute * attrs,int attrCount)249     void setVertexAttributesWithImplicitOffsets(const Attribute* attrs, int attrCount) {
250         fVertexAttributes.initImplicit(attrs, attrCount);
251     }
setInstanceAttributesWithImplicitOffsets(const Attribute * attrs,int attrCount)252     void setInstanceAttributesWithImplicitOffsets(const Attribute* attrs, int attrCount) {
253         SkASSERT(attrCount >= 0);
254         fInstanceAttributes.initImplicit(attrs, attrCount);
255     }
setTextureSamplerCnt(int cnt)256     void setTextureSamplerCnt(int cnt) {
257         SkASSERT(cnt >= 0);
258         fTextureSamplerCnt = cnt;
259     }
260 
261 private:
onTextureSampler(int)262     virtual const TextureSampler& onTextureSampler(int) const { SK_ABORT("no texture samplers"); }
263 
264     AttributeSet fVertexAttributes;
265     AttributeSet fInstanceAttributes;
266 
267     int fTextureSamplerCnt = 0;
268     using INHERITED = GrProcessor;
269 };
270 
271 //////////////////////////////////////////////////////////////////////////////
272 
273 class GrGeometryProcessor::ProgramImpl {
274 public:
275     using UniformHandle = GrGLSLProgramDataManager::UniformHandle;
276     using SamplerHandle = GrGLSLUniformHandler::SamplerHandle;
277     /**
278      * Struct of optional varying that replaces the input coords and bool indicating whether the FP
279      * should take a coord param as an argument. The latter may be false if the coords are simply
280      * unused or if the GP has lifted their computation to a varying emitted by the VS.
281      */
282     struct FPCoords {GrShaderVar coordsVarying; bool hasCoordsParam;};
283     using FPCoordsMap = std::unordered_map<const GrFragmentProcessor*, FPCoords>;
284 
285     virtual ~ProgramImpl() = default;
286 
287     struct EmitArgs {
EmitArgsEmitArgs288         EmitArgs(GrGLSLVertexBuilder* vertBuilder,
289                  GrGLSLFPFragmentBuilder* fragBuilder,
290                  GrGLSLVaryingHandler* varyingHandler,
291                  GrGLSLUniformHandler* uniformHandler,
292                  const GrShaderCaps* caps,
293                  const GrGeometryProcessor& geomProc,
294                  const char* outputColor,
295                  const char* outputCoverage,
296                  const SamplerHandle* texSamplers)
297                 : fVertBuilder(vertBuilder)
298                 , fFragBuilder(fragBuilder)
299                 , fVaryingHandler(varyingHandler)
300                 , fUniformHandler(uniformHandler)
301                 , fShaderCaps(caps)
302                 , fGeomProc(geomProc)
303                 , fOutputColor(outputColor)
304                 , fOutputCoverage(outputCoverage)
305                 , fTexSamplers(texSamplers) {}
306         GrGLSLVertexBuilder* fVertBuilder;
307         GrGLSLFPFragmentBuilder* fFragBuilder;
308         GrGLSLVaryingHandler* fVaryingHandler;
309         GrGLSLUniformHandler* fUniformHandler;
310         const GrShaderCaps* fShaderCaps;
311         const GrGeometryProcessor& fGeomProc;
312         const char* fOutputColor;
313         const char* fOutputCoverage;
314         const SamplerHandle* fTexSamplers;
315     };
316 
317     /**
318      * Emits the code from this geometry processor into the shaders. For any FP in the pipeline that
319      * has its input coords implemented by the GP as a varying, the varying will be accessible in
320      * the returned map and should be used when the FP code is emitted. The FS variable containing
321      * the GP's output local coords is also returned.
322      **/
323     std::tuple<FPCoordsMap, GrShaderVar> emitCode(EmitArgs&, const GrPipeline& pipeline);
324 
325     /**
326      * Called after all effect emitCode() functions, to give the processor a chance to write out
327      * additional transformation code now that all uniforms have been emitted.
328      * It generates the final code for assigning transformed coordinates to the varyings recorded
329      * in the call to collectTransforms(). This must happen after FP code emission so that it has
330      * access to any uniforms the FPs registered for uniform sample matrix invocations.
331      */
332     void emitTransformCode(GrGLSLVertexBuilder* vb, GrGLSLUniformHandler* uniformHandler);
333 
334     /**
335      * A ProgramImpl instance can be reused with any GrGeometryProcessor that produces the same key.
336      * This function reads data from a GrGeometryProcessor and updates any uniform variables
337      * required by the shaders created in emitCode(). The GrGeometryProcessor parameter is
338      * guaranteed to be of the same type and to have an identical processor key as the
339      * GrGeometryProcessor that created this ProgramImpl.
340      */
341     virtual void setData(const GrGLSLProgramDataManager&,
342                          const GrShaderCaps&,
343                          const GrGeometryProcessor&) = 0;
344 
345     // GPs that use writeOutputPosition and/or writeLocalCoord must incorporate the matrix type
346     // into their key, and should use this function or one of the other related helpers.
ComputeMatrixKey(const GrShaderCaps & caps,const SkMatrix & mat)347     static uint32_t ComputeMatrixKey(const GrShaderCaps& caps, const SkMatrix& mat) {
348         if (!caps.fReducedShaderMode) {
349             if (mat.isIdentity()) {
350                 return 0b00;
351             }
352             if (mat.isScaleTranslate()) {
353                 return 0b01;
354             }
355         }
356         if (!mat.hasPerspective()) {
357             return 0b10;
358         }
359         return 0b11;
360     }
361 
ComputeMatrixKeys(const GrShaderCaps & shaderCaps,const SkMatrix & viewMatrix,const SkMatrix & localMatrix)362     static uint32_t ComputeMatrixKeys(const GrShaderCaps& shaderCaps,
363                                       const SkMatrix& viewMatrix,
364                                       const SkMatrix& localMatrix) {
365         return (ComputeMatrixKey(shaderCaps, viewMatrix) << kMatrixKeyBits) |
366                ComputeMatrixKey(shaderCaps, localMatrix);
367     }
368 
AddMatrixKeys(const GrShaderCaps & shaderCaps,uint32_t flags,const SkMatrix & viewMatrix,const SkMatrix & localMatrix)369     static uint32_t AddMatrixKeys(const GrShaderCaps& shaderCaps,
370                                   uint32_t flags,
371                                   const SkMatrix& viewMatrix,
372                                   const SkMatrix& localMatrix) {
373         // Shifting to make room for the matrix keys shouldn't lose bits
374         SkASSERT(((flags << (2 * kMatrixKeyBits)) >> (2 * kMatrixKeyBits)) == flags);
375         return (flags << (2 * kMatrixKeyBits)) |
376                ComputeMatrixKeys(shaderCaps, viewMatrix, localMatrix);
377     }
378     inline static constexpr int kMatrixKeyBits = 2;
379 
380 protected:
381     void setupUniformColor(GrGLSLFPFragmentBuilder* fragBuilder,
382                            GrGLSLUniformHandler* uniformHandler,
383                            const char* outputName,
384                            UniformHandle* colorUniform);
385 
386     // A helper for setting the matrix on a uniform handle initialized through
387     // writeOutputPosition or writeLocalCoord. Automatically handles elided uniforms,
388     // scale+translate matrices, and state tracking (if provided state pointer is non-null).
389     static void SetTransform(const GrGLSLProgramDataManager&,
390                              const GrShaderCaps&,
391                              const UniformHandle& uniform,
392                              const SkMatrix& matrix,
393                              SkMatrix* state = nullptr);
394 
395     struct GrGPArgs {
396         // Used to specify the output variable used by the GP to store its device position. It can
397         // either be a float2 or a float3 (in order to handle perspective). The subclass sets this
398         // in its onEmitCode().
399         GrShaderVar fPositionVar;
400         // Used to specify the variable storing the draw's local coordinates. It can be either a
401         // float2, float3, or void. It can only be void when no FP needs local coordinates. This
402         // variable can be an attribute or local variable, but should not itself be a varying.
403         // ProgramImpl automatically determines if this must be passed to a FS.
404         GrShaderVar fLocalCoordVar;
405         // The GP can specify the local coord var either in the VS or FS. When either is possible
406         // the VS is preferable. It may allow derived coordinates to be interpolated from the VS
407         // instead of computed in the FS per pixel.
408         GrShaderType fLocalCoordShader = kVertex_GrShaderType;
409     };
410 
411     // Helpers for adding code to write the transformed vertex position. The first simple version
412     // just writes a variable named by 'posName' into the position output variable with the
413     // assumption that the position is 2D. The second version transforms the input position by a
414     // view matrix and the output variable is 2D or 3D depending on whether the view matrix is
415     // perspective. Both versions declare the output position variable and will set
416     // GrGPArgs::fPositionVar.
417     static void WriteOutputPosition(GrGLSLVertexBuilder*, GrGPArgs*, const char* posName);
418     static void WriteOutputPosition(GrGLSLVertexBuilder*,
419                                     GrGLSLUniformHandler*,
420                                     const GrShaderCaps&,
421                                     GrGPArgs*,
422                                     const char* posName,
423                                     const SkMatrix& viewMatrix,
424                                     UniformHandle* viewMatrixUniform);
425 
426     // Helper to transform an existing variable by a given local matrix (e.g. the inverse view
427     // matrix). It will declare the transformed local coord variable and will set
428     // GrGPArgs::fLocalCoordVar.
429     static void WriteLocalCoord(GrGLSLVertexBuilder*,
430                                 GrGLSLUniformHandler*,
431                                 const GrShaderCaps&,
432                                 GrGPArgs*,
433                                 GrShaderVar localVar,
434                                 const SkMatrix& localMatrix,
435                                 UniformHandle* localMatrixUniform);
436 
437 private:
438     virtual void onEmitCode(EmitArgs&, GrGPArgs*) = 0;
439 
440     // Iterates over the FPs beginning with the passed iter to register additional varyings and
441     // uniforms to support VS-promoted local coord evaluation for the FPs.
442     //
443     // This must happen before FP code emission so that the FPs can find the appropriate varying
444     // handles they use in place of explicit coord sampling; it is automatically called after
445     // onEmitCode() returns using the value stored in GpArgs::fLocalCoordVar and
446     // GpArgs::fPositionVar.
447     FPCoordsMap collectTransforms(GrGLSLVertexBuilder* vb,
448                                   GrGLSLVaryingHandler* varyingHandler,
449                                   GrGLSLUniformHandler* uniformHandler,
450                                   GrShaderType localCoordsShader,
451                                   const GrShaderVar& localCoordsVar,
452                                   const GrShaderVar& positionVar,
453                                   const GrPipeline& pipeline);
454     struct TransformInfo {
455         // The varying that conveys the coordinates to one or more FPs in the FS.
456         GrGLSLVarying varying;
457         // The coordinate to be transformed. varying is computed from this.
458         GrShaderVar   inputCoords;
459         // Used to sort so that ancestor FP varyings are initialized before descendant FP varyings.
460         int           traversalOrder;
461     };
462     // Populated by collectTransforms() for use in emitTransformCode(). When we lift the computation
463     // of a FP's input coord to a varying we propagate that varying up the FP tree to the highest
464     // node that shares the same coordinates. This allows multiple FPs in a subtree to share a
465     // varying.
466     std::unordered_map<const GrFragmentProcessor*, TransformInfo> fTransformVaryingsMap;
467 
468     // Move back into collectTransforms when /std=c++20 can be used with msvc.
469     enum class BaseCoord { kNone, kLocal, kPosition };
470 };
471 
472 ///////////////////////////////////////////////////////////////////////////
473 
474 /**
475  * Used to capture the properties of the GrTextureProxies required/expected by a primitiveProcessor
476  * along with an associated GrSamplerState. The actual proxies used are stored in either the
477  * fixed or dynamic state arrays. TextureSamplers don't perform any coord manipulation to account
478  * for texture origin.
479  */
480 class GrGeometryProcessor::TextureSampler {
481 public:
482     TextureSampler() = default;
483 
484     TextureSampler(GrSamplerState, const GrBackendFormat&, const skgpu::Swizzle&);
485 
486     TextureSampler(const TextureSampler&) = delete;
487     TextureSampler& operator=(const TextureSampler&) = delete;
488 
489     TextureSampler(TextureSampler&&) = default;
490     TextureSampler& operator=(TextureSampler&&) = default;
491 
492     void reset(GrSamplerState, const GrBackendFormat&, const skgpu::Swizzle&);
493 
backendFormat()494     const GrBackendFormat& backendFormat() const { return fBackendFormat; }
textureType()495     GrTextureType textureType() const { return fBackendFormat.textureType(); }
496 
samplerState()497     GrSamplerState samplerState() const { return fSamplerState; }
swizzle()498     const skgpu::Swizzle& swizzle() const { return fSwizzle; }
499 
isInitialized()500     bool isInitialized() const { return fIsInitialized; }
501 
502 private:
503     GrSamplerState  fSamplerState;
504     GrBackendFormat fBackendFormat;
505     skgpu::Swizzle  fSwizzle;
506     bool            fIsInitialized = false;
507 };
508 
509 //////////////////////////////////////////////////////////////////////////////
510 
511 /**
512  * Returns the size of the attrib type in bytes.
513  * This was moved from include/private/gpu/ganesh/GrTypesPriv.h in service of Skia dependents that build
514  * with C++11.
515  */
GrVertexAttribTypeSize(GrVertexAttribType type)516 static constexpr inline size_t GrVertexAttribTypeSize(GrVertexAttribType type) {
517     switch (type) {
518         case kFloat_GrVertexAttribType:
519             return sizeof(float);
520         case kFloat2_GrVertexAttribType:
521             return 2 * sizeof(float);
522         case kFloat3_GrVertexAttribType:
523             return 3 * sizeof(float);
524         case kFloat4_GrVertexAttribType:
525             return 4 * sizeof(float);
526         case kHalf_GrVertexAttribType:
527             return sizeof(uint16_t);
528         case kHalf2_GrVertexAttribType:
529             return 2 * sizeof(uint16_t);
530         case kHalf4_GrVertexAttribType:
531             return 4 * sizeof(uint16_t);
532         case kInt2_GrVertexAttribType:
533             return 2 * sizeof(int32_t);
534         case kInt3_GrVertexAttribType:
535             return 3 * sizeof(int32_t);
536         case kInt4_GrVertexAttribType:
537             return 4 * sizeof(int32_t);
538         case kByte_GrVertexAttribType:
539             return 1 * sizeof(char);
540         case kByte2_GrVertexAttribType:
541             return 2 * sizeof(char);
542         case kByte4_GrVertexAttribType:
543             return 4 * sizeof(char);
544         case kUByte_GrVertexAttribType:
545             return 1 * sizeof(char);
546         case kUByte2_GrVertexAttribType:
547             return 2 * sizeof(char);
548         case kUByte4_GrVertexAttribType:
549             return 4 * sizeof(char);
550         case kUByte_norm_GrVertexAttribType:
551             return 1 * sizeof(char);
552         case kUByte4_norm_GrVertexAttribType:
553             return 4 * sizeof(char);
554         case kShort2_GrVertexAttribType:
555             return 2 * sizeof(int16_t);
556         case kShort4_GrVertexAttribType:
557             return 4 * sizeof(int16_t);
558         case kUShort2_GrVertexAttribType: // fall through
559         case kUShort2_norm_GrVertexAttribType:
560             return 2 * sizeof(uint16_t);
561         case kInt_GrVertexAttribType:
562             return sizeof(int32_t);
563         case kUInt_GrVertexAttribType:
564             return sizeof(uint32_t);
565         case kUShort_norm_GrVertexAttribType:
566             return sizeof(uint16_t);
567         case kUShort4_norm_GrVertexAttribType:
568             return 4 * sizeof(uint16_t);
569     }
570     // GCC fails because SK_ABORT evaluates to non constexpr. clang and cl.exe think this is
571     // unreachable and don't complain.
572 #if defined(__clang__) || !defined(__GNUC__)
573     SK_ABORT("Unsupported type conversion");
574 #endif
575     return 0;
576 }
577 
size()578 constexpr size_t GrGeometryProcessor::Attribute::size() const {
579     return GrVertexAttribTypeSize(fCPUType);
580 }
581 
582 #endif
583