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