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