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