1 | /* |
2 | * Copyright 2015 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 | #include "include/core/SkM44.h" |
9 | #include "include/effects/SkRuntimeEffect.h" |
10 | #include "src/core/SkArenaAlloc.h" |
11 | #include "src/core/SkVerticesPriv.h" |
12 | #include "src/gpu/GrOpFlushState.h" |
13 | #include "src/gpu/GrProgramInfo.h" |
14 | #include "src/gpu/GrVertexWriter.h" |
15 | #include "src/gpu/SkGr.h" |
16 | #include "src/gpu/glsl/GrGLSLColorSpaceXformHelper.h" |
17 | #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h" |
18 | #include "src/gpu/glsl/GrGLSLGeometryProcessor.h" |
19 | #include "src/gpu/glsl/GrGLSLVarying.h" |
20 | #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h" |
21 | #include "src/gpu/ops/GrDrawVerticesOp.h" |
22 | #include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h" |
23 | |
24 | namespace { |
25 | |
26 | enum class ColorArrayType { |
27 | kUnused, |
28 | kPremulGrColor, |
29 | kSkColor, |
30 | }; |
31 | |
32 | enum class LocalCoordsType { |
33 | kUnused, |
34 | kUsePosition, |
35 | kExplicit, |
36 | }; |
37 | |
38 | static GrVertexAttribType SkVerticesAttributeToGrVertexAttribType(const SkVertices::Attribute& a) { |
39 | switch (a.fType) { |
40 | case SkVertices::Attribute::Type::kFloat: return kFloat_GrVertexAttribType; |
41 | case SkVertices::Attribute::Type::kFloat2: return kFloat2_GrVertexAttribType; |
42 | case SkVertices::Attribute::Type::kFloat3: return kFloat3_GrVertexAttribType; |
43 | case SkVertices::Attribute::Type::kFloat4: return kFloat4_GrVertexAttribType; |
44 | case SkVertices::Attribute::Type::kByte4_unorm: return kUByte4_norm_GrVertexAttribType; |
45 | } |
46 | SkUNREACHABLE; |
47 | } |
48 | |
49 | static GrSLType SkVerticesAttributeToGrSLType(const SkVertices::Attribute& a) { |
50 | switch (a.fType) { |
51 | case SkVertices::Attribute::Type::kFloat: return kFloat_GrSLType; |
52 | case SkVertices::Attribute::Type::kFloat2: return kFloat2_GrSLType; |
53 | case SkVertices::Attribute::Type::kFloat3: return kFloat3_GrSLType; |
54 | case SkVertices::Attribute::Type::kFloat4: return kFloat4_GrSLType; |
55 | case SkVertices::Attribute::Type::kByte4_unorm: return kHalf4_GrSLType; |
56 | } |
57 | SkUNREACHABLE; |
58 | } |
59 | |
60 | class VerticesGP : public GrGeometryProcessor { |
61 | public: |
62 | static GrGeometryProcessor* Make(SkArenaAlloc* arena, |
63 | LocalCoordsType localCoordsType, |
64 | ColorArrayType colorArrayType, |
65 | const SkPMColor4f& color, |
66 | sk_sp<GrColorSpaceXform> colorSpaceXform, |
67 | const SkMatrix& viewMatrix, |
68 | const SkVertices::Attribute* attrs, |
69 | int attrCount, |
70 | const SkM44& localToWorld) { |
71 | return arena->make<VerticesGP>(localCoordsType, colorArrayType, color, |
72 | std::move(colorSpaceXform), viewMatrix, attrs, attrCount, |
73 | localToWorld); |
74 | } |
75 | |
76 | const char* name() const override { return "VerticesGP" ; } |
77 | |
78 | const SkPMColor4f& color() const { return fColor; } |
79 | const SkMatrix& viewMatrix() const { return fViewMatrix; } |
80 | |
81 | const Attribute& positionAttr() const { return fAttributes[kPositionIndex]; } |
82 | const Attribute& colorAttr() const { return fAttributes[kColorIndex]; } |
83 | const Attribute& localCoordsAttr() const { return fAttributes[kLocalCoordsIndex]; } |
84 | |
85 | class GLSLProcessor : public GrGLSLGeometryProcessor { |
86 | public: |
87 | GLSLProcessor() |
88 | : fViewMatrix(SkMatrix::InvalidMatrix()) |
89 | , fColor(SK_PMColor4fILLEGAL) {} |
90 | |
91 | void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { |
92 | const VerticesGP& gp = args.fGP.cast<VerticesGP>(); |
93 | GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; |
94 | GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; |
95 | GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; |
96 | GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; |
97 | |
98 | // emit attributes |
99 | varyingHandler->emitAttributes(gp); |
100 | |
101 | fColorSpaceHelper.emitCode(uniformHandler, gp.fColorSpaceXform.get(), |
102 | kVertex_GrShaderFlag); |
103 | |
104 | // Setup pass through color |
105 | if (gp.colorAttr().isInitialized()) { |
106 | GrGLSLVarying varying(kHalf4_GrSLType); |
107 | varyingHandler->addVarying("color" , &varying); |
108 | vertBuilder->codeAppendf("half4 color = %s;" , gp.colorAttr().name()); |
109 | |
110 | // For SkColor, do a red/blue swap, possible color space conversion, and premul |
111 | if (gp.fColorArrayType == ColorArrayType::kSkColor) { |
112 | vertBuilder->codeAppend("color = color.bgra;" ); |
113 | |
114 | SkString xformedColor; |
115 | vertBuilder->appendColorGamutXform(&xformedColor, "color" , &fColorSpaceHelper); |
116 | vertBuilder->codeAppendf("color = %s;" , xformedColor.c_str()); |
117 | |
118 | vertBuilder->codeAppend("color = half4(color.rgb * color.a, color.a);" ); |
119 | } |
120 | |
121 | vertBuilder->codeAppendf("%s = color;\n" , varying.vsOut()); |
122 | fragBuilder->codeAppendf("%s = %s;" , args.fOutputColor, varying.fsIn()); |
123 | } else { |
124 | this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor, |
125 | &fColorUniform); |
126 | } |
127 | |
128 | // Setup position |
129 | this->writeOutputPosition(vertBuilder, |
130 | uniformHandler, |
131 | gpArgs, |
132 | gp.positionAttr().name(), |
133 | gp.viewMatrix(), |
134 | &fViewMatrixUniform); |
135 | |
136 | // emit transforms using either explicit local coords or positions |
137 | const auto& coordsAttr = gp.localCoordsAttr().isInitialized() ? gp.localCoordsAttr() |
138 | : gp.positionAttr(); |
139 | this->emitTransforms(vertBuilder, |
140 | varyingHandler, |
141 | uniformHandler, |
142 | coordsAttr.asShaderVar(), |
143 | SkMatrix::I(), |
144 | args.fFPCoordTransformHandler); |
145 | |
146 | // Add varyings and globals for all custom attributes |
147 | for (size_t i = kFirstCustomIndex; i < gp.fAttributes.size(); ++i) { |
148 | const auto& attr(gp.fAttributes[i]); |
149 | const int customIdx = i - kFirstCustomIndex; |
150 | auto usage = gp.fUsages[customIdx]; |
151 | |
152 | GrSLType varyingType = attr.gpuType(); |
153 | SkString varyingIn(attr.name()); |
154 | |
155 | switch (usage) { |
156 | case SkVertices::Attribute::Usage::kRaw: |
157 | break; |
158 | case SkVertices::Attribute::Usage::kColor: { |
159 | // For RGB colors, expand to RGBA with A = 1 |
160 | if (attr.gpuType() == kFloat3_GrSLType) { |
161 | varyingIn = SkStringPrintf("float4(%s, 1)" , attr.name()); |
162 | } |
163 | // Convert to half (as expected by the color space transform functions) |
164 | varyingIn = SkStringPrintf("half4(%s)" , varyingIn.c_str()); |
165 | // Transform to destination color space (possible no-op) |
166 | SkString xformedColor; |
167 | vertBuilder->appendColorGamutXform(&xformedColor, varyingIn.c_str(), |
168 | &fColorSpaceHelper); |
169 | // Store the result of the transform in a temporary |
170 | vertBuilder->codeAppendf( |
171 | "half4 _tmp_clr_%d = %s;" , customIdx, xformedColor.c_str()); |
172 | // Finally, premultiply |
173 | varyingIn = SkStringPrintf( |
174 | "half4(_tmp_clr_%d.rgb * _tmp_clr_%d.a, _tmp_clr_%d.a)" , |
175 | customIdx, customIdx, customIdx); |
176 | varyingType = kHalf4_GrSLType; |
177 | break; |
178 | } |
179 | case SkVertices::Attribute::Usage::kVector: { |
180 | if (!fLocalToWorldMatrixUniform.isValid()) { |
181 | fLocalToWorldMatrixUniform = |
182 | uniformHandler->addUniform(nullptr, kVertex_GrShaderFlag, |
183 | kFloat4x4_GrSLType, "localToWorld" ); |
184 | } |
185 | if (attr.gpuType() == kFloat2_GrSLType) { |
186 | varyingIn = SkStringPrintf("float3(%s, 0)" , attr.name()); |
187 | } |
188 | varyingIn = SkStringPrintf( |
189 | "normalize((%s * float4(%s, 0)).xyz)" , |
190 | uniformHandler->getUniformCStr(fLocalToWorldMatrixUniform), |
191 | varyingIn.c_str()); |
192 | varyingType = kFloat3_GrSLType; |
193 | break; |
194 | } |
195 | case SkVertices::Attribute::Usage::kNormalVector: { |
196 | if (!fNormalMatrixUniform.isValid()) { |
197 | fNormalMatrixUniform = uniformHandler->addUniform( |
198 | nullptr, kVertex_GrShaderFlag, kFloat3x3_GrSLType, |
199 | "localToWorld_IT" ); |
200 | } |
201 | if (attr.gpuType() == kFloat2_GrSLType) { |
202 | varyingIn = SkStringPrintf("float3(%s, 0)" , attr.name()); |
203 | } |
204 | varyingIn = SkStringPrintf( |
205 | "normalize(%s * %s)" , |
206 | uniformHandler->getUniformCStr(fNormalMatrixUniform), |
207 | varyingIn.c_str()); |
208 | varyingType = kFloat3_GrSLType; |
209 | break; |
210 | } |
211 | case SkVertices::Attribute::Usage::kPosition: { |
212 | if (!fLocalToWorldMatrixUniform.isValid()) { |
213 | fLocalToWorldMatrixUniform = |
214 | uniformHandler->addUniform(nullptr, kVertex_GrShaderFlag, |
215 | kFloat4x4_GrSLType, "localToWorld" ); |
216 | } |
217 | if (attr.gpuType() == kFloat2_GrSLType) { |
218 | varyingIn = SkStringPrintf("float3(%s, 0)" , attr.name()); |
219 | } |
220 | vertBuilder->codeAppendf( |
221 | "float4 _tmp_pos_%d = %s * float4(%s, 1);" , |
222 | customIdx, |
223 | uniformHandler->getUniformCStr(fLocalToWorldMatrixUniform), |
224 | varyingIn.c_str()); |
225 | varyingIn = SkStringPrintf("_tmp_pos_%d.xyz / _tmp_pos_%d.w" , |
226 | customIdx, customIdx); |
227 | varyingType = kFloat3_GrSLType; |
228 | } |
229 | } |
230 | |
231 | GrGLSLVarying varying(varyingType); |
232 | varyingHandler->addVarying(attr.name(), &varying); |
233 | vertBuilder->codeAppendf("%s = %s;" , varying.vsOut(), varyingIn.c_str()); |
234 | |
235 | GrShaderVar var(SkStringPrintf("_vtx_attr_%d" , customIdx), varyingType); |
236 | fragBuilder->declareGlobal(var); |
237 | fragBuilder->codeAppendf("%s = %s;" , var.c_str(), varying.fsIn()); |
238 | } |
239 | |
240 | fragBuilder->codeAppendf("%s = half4(1);" , args.fOutputCoverage); |
241 | } |
242 | |
243 | static inline void GenKey(const GrGeometryProcessor& gp, |
244 | const GrShaderCaps&, |
245 | GrProcessorKeyBuilder* b) { |
246 | const VerticesGP& vgp = gp.cast<VerticesGP>(); |
247 | uint32_t key = 0; |
248 | key |= (vgp.fColorArrayType == ColorArrayType::kSkColor) ? 0x1 : 0; |
249 | key |= ComputePosKey(vgp.viewMatrix()) << 20; |
250 | b->add32(key); |
251 | b->add32(GrColorSpaceXform::XformKey(vgp.fColorSpaceXform.get())); |
252 | |
253 | uint32_t usageKey = 0; |
254 | SkASSERT(vgp.fUsages.size() <= 8); |
255 | for (size_t i = 0; i < vgp.fUsages.size(); ++i) { |
256 | SkASSERT((uint32_t)vgp.fUsages[i] < (1 << 4)); |
257 | usageKey = (usageKey << 4) | (uint32_t)vgp.fUsages[i]; |
258 | } |
259 | b->add32(usageKey); |
260 | } |
261 | |
262 | void setData(const GrGLSLProgramDataManager& pdman, |
263 | const GrPrimitiveProcessor& gp, |
264 | const CoordTransformRange& transformRange) override { |
265 | const VerticesGP& vgp = gp.cast<VerticesGP>(); |
266 | |
267 | if (!vgp.viewMatrix().isIdentity() && |
268 | !SkMatrixPriv::CheapEqual(fViewMatrix, vgp.viewMatrix())) { |
269 | fViewMatrix = vgp.viewMatrix(); |
270 | pdman.setSkMatrix(fViewMatrixUniform, fViewMatrix); |
271 | } |
272 | |
273 | if (!vgp.colorAttr().isInitialized() && vgp.color() != fColor) { |
274 | pdman.set4fv(fColorUniform, 1, vgp.color().vec()); |
275 | fColor = vgp.color(); |
276 | } |
277 | |
278 | this->setTransformDataHelper(SkMatrix::I(), pdman, transformRange); |
279 | |
280 | fColorSpaceHelper.setData(pdman, vgp.fColorSpaceXform.get()); |
281 | |
282 | if (fLocalToWorldMatrixUniform.isValid()) { |
283 | pdman.setSkM44(fLocalToWorldMatrixUniform, vgp.fLocalToWorldMatrix); |
284 | } |
285 | |
286 | if (fNormalMatrixUniform.isValid()) { |
287 | // Get the upper-left 3x3 (rotation + scale) of local to world: |
288 | SkM44 mtx(vgp.fLocalToWorldMatrix); |
289 | mtx.setCol(3, {0, 0, 0, 1}); |
290 | mtx.setRow(3, {0, 0, 0, 1}); |
291 | // Invert it... |
292 | SkAssertResult(mtx.invert(&mtx)); |
293 | // We want the inverse transpose, but we're going to feed it as a 3x3 column major |
294 | // matrix to the uniform. So copy the (not-yet-transposed) values out in row order. |
295 | float l2wIT[9] = { mtx.rc(0, 0), mtx.rc(0, 1), mtx.rc(0, 2), |
296 | mtx.rc(1, 0), mtx.rc(1, 1), mtx.rc(1, 2), |
297 | mtx.rc(2, 0), mtx.rc(2, 1), mtx.rc(2, 2) }; |
298 | pdman.setMatrix3f(fNormalMatrixUniform, l2wIT); |
299 | } |
300 | } |
301 | |
302 | private: |
303 | SkMatrix fViewMatrix; |
304 | SkPMColor4f fColor; |
305 | UniformHandle fViewMatrixUniform; |
306 | UniformHandle fColorUniform; |
307 | GrGLSLColorSpaceXformHelper fColorSpaceHelper; |
308 | |
309 | UniformHandle fLocalToWorldMatrixUniform; |
310 | UniformHandle fNormalMatrixUniform; |
311 | |
312 | typedef GrGLSLGeometryProcessor INHERITED; |
313 | }; |
314 | |
315 | void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override { |
316 | GLSLProcessor::GenKey(*this, caps, b); |
317 | } |
318 | |
319 | GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override { |
320 | return new GLSLProcessor(); |
321 | } |
322 | |
323 | private: |
324 | friend class ::SkArenaAlloc; // for access to ctor |
325 | |
326 | VerticesGP(LocalCoordsType localCoordsType, |
327 | ColorArrayType colorArrayType, |
328 | const SkPMColor4f& color, |
329 | sk_sp<GrColorSpaceXform> colorSpaceXform, |
330 | const SkMatrix& viewMatrix, |
331 | const SkVertices::Attribute* attrs, |
332 | int attrCount, |
333 | const SkM44& localToWorld) |
334 | : INHERITED(kVerticesGP_ClassID) |
335 | , fColorArrayType(colorArrayType) |
336 | , fColor(color) |
337 | , fViewMatrix(viewMatrix) |
338 | , fColorSpaceXform(std::move(colorSpaceXform)) |
339 | , fLocalToWorldMatrix(localToWorld) { |
340 | constexpr Attribute missingAttr; |
341 | fAttributes.push_back({"position" , kFloat2_GrVertexAttribType, kFloat2_GrSLType}); |
342 | fAttributes.push_back(fColorArrayType != ColorArrayType::kUnused |
343 | ? MakeColorAttribute("inColor" , false) |
344 | : missingAttr); |
345 | fAttributes.push_back(localCoordsType == LocalCoordsType::kExplicit |
346 | ? Attribute{"inLocalCoord" , kFloat2_GrVertexAttribType, kFloat2_GrSLType} |
347 | : missingAttr); |
348 | |
349 | for (int i = 0; i < attrCount; ++i) { |
350 | // Attributes store char*, so allocate long-lived storage for the (dynamic) names |
351 | fAttrNames.push_back(SkStringPrintf("_vtx_attr%d" , i)); |
352 | fAttributes.push_back({fAttrNames.back().c_str(), |
353 | SkVerticesAttributeToGrVertexAttribType(attrs[i]), |
354 | SkVerticesAttributeToGrSLType(attrs[i])}); |
355 | fUsages.push_back(attrs[i].fUsage); |
356 | } |
357 | |
358 | this->setVertexAttributes(fAttributes.data(), fAttributes.size()); |
359 | } |
360 | |
361 | enum { |
362 | kPositionIndex = 0, |
363 | kColorIndex = 1, |
364 | kLocalCoordsIndex = 2, |
365 | kFirstCustomIndex = 3, |
366 | }; |
367 | |
368 | std::vector<SkString> fAttrNames; |
369 | std::vector<Attribute> fAttributes; |
370 | std::vector<SkVertices::Attribute::Usage> fUsages; |
371 | ColorArrayType fColorArrayType; |
372 | SkPMColor4f fColor; |
373 | SkMatrix fViewMatrix; |
374 | sk_sp<GrColorSpaceXform> fColorSpaceXform; |
375 | SkM44 fLocalToWorldMatrix; |
376 | |
377 | typedef GrGeometryProcessor INHERITED; |
378 | }; |
379 | |
380 | class DrawVerticesOp final : public GrMeshDrawOp { |
381 | private: |
382 | using Helper = GrSimpleMeshDrawOpHelper; |
383 | |
384 | public: |
385 | DEFINE_OP_CLASS_ID |
386 | |
387 | DrawVerticesOp(const Helper::MakeArgs&, const SkPMColor4f&, sk_sp<SkVertices>, |
388 | GrPrimitiveType, GrAAType, sk_sp<GrColorSpaceXform>, const SkMatrix& viewMatrix, |
389 | const SkRuntimeEffect*, const SkM44* localToWorld); |
390 | |
391 | const char* name() const override { return "DrawVerticesOp" ; } |
392 | |
393 | void visitProxies(const VisitProxyFunc& func) const override { |
394 | if (fProgramInfo) { |
395 | fProgramInfo->visitFPProxies(func); |
396 | } else { |
397 | fHelper.visitProxies(func); |
398 | } |
399 | } |
400 | |
401 | #ifdef SK_DEBUG |
402 | SkString dumpInfo() const override; |
403 | #endif |
404 | |
405 | FixedFunctionFlags fixedFunctionFlags() const override; |
406 | |
407 | GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, |
408 | bool hasMixedSampledCoverage, GrClampType) override; |
409 | |
410 | private: |
411 | GrProgramInfo* programInfo() override { return fProgramInfo; } |
412 | |
413 | void onCreateProgramInfo(const GrCaps*, |
414 | SkArenaAlloc*, |
415 | const GrSurfaceProxyView* writeView, |
416 | GrAppliedClip&&, |
417 | const GrXferProcessor::DstProxyView&) override; |
418 | |
419 | void onPrepareDraws(Target*) override; |
420 | void onExecute(GrOpFlushState*, const SkRect& chainBounds) override; |
421 | |
422 | GrGeometryProcessor* makeGP(SkArenaAlloc*); |
423 | |
424 | GrPrimitiveType primitiveType() const { return fPrimitiveType; } |
425 | bool combinablePrimitive() const { |
426 | return GrPrimitiveType::kTriangles == fPrimitiveType || |
427 | GrPrimitiveType::kLines == fPrimitiveType || |
428 | GrPrimitiveType::kPoints == fPrimitiveType; |
429 | } |
430 | |
431 | CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*, const GrCaps&) override; |
432 | |
433 | struct Mesh { |
434 | SkPMColor4f fColor; // Used if this->hasPerVertexColors() is false. |
435 | sk_sp<SkVertices> fVertices; |
436 | SkMatrix fViewMatrix; |
437 | bool fIgnoreColors; |
438 | |
439 | bool hasPerVertexColors() const { |
440 | return fVertices->priv().hasColors() && !fIgnoreColors; |
441 | } |
442 | }; |
443 | |
444 | bool isIndexed() const { |
445 | // Consistency enforced in onCombineIfPossible. |
446 | return fMeshes[0].fVertices->priv().hasIndices(); |
447 | } |
448 | |
449 | bool requiresPerVertexColors() const { |
450 | return fColorArrayType != ColorArrayType::kUnused; |
451 | } |
452 | |
453 | bool requiresPerVertexLocalCoords() const { |
454 | return fLocalCoordsType == LocalCoordsType::kExplicit; |
455 | } |
456 | |
457 | size_t vertexStride() const { |
458 | return sizeof(SkPoint) + |
459 | (this->requiresPerVertexColors() ? sizeof(uint32_t) : 0) + |
460 | (this->requiresPerVertexLocalCoords() ? sizeof(SkPoint) : 0) + |
461 | fMeshes[0].fVertices->priv().customDataSize(); |
462 | } |
463 | |
464 | Helper fHelper; |
465 | SkSTArray<1, Mesh, true> fMeshes; |
466 | // GrPrimitiveType is more expressive than fVertices.mode() so it is used instead and we ignore |
467 | // the SkVertices mode (though fPrimitiveType may have been inferred from it). |
468 | GrPrimitiveType fPrimitiveType; |
469 | int fVertexCount; |
470 | int fIndexCount; |
471 | bool fMultipleViewMatrices; |
472 | LocalCoordsType fLocalCoordsType; |
473 | ColorArrayType fColorArrayType; |
474 | sk_sp<GrColorSpaceXform> fColorSpaceXform; |
475 | SkM44 fLocalToWorld; |
476 | |
477 | GrSimpleMesh* fMesh = nullptr; |
478 | GrProgramInfo* fProgramInfo = nullptr; |
479 | |
480 | typedef GrMeshDrawOp INHERITED; |
481 | }; |
482 | |
483 | DrawVerticesOp::DrawVerticesOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color, |
484 | sk_sp<SkVertices> vertices, GrPrimitiveType primitiveType, |
485 | GrAAType aaType, sk_sp<GrColorSpaceXform> colorSpaceXform, |
486 | const SkMatrix& viewMatrix, const SkRuntimeEffect* effect, |
487 | const SkM44* localToWorld) |
488 | : INHERITED(ClassID()) |
489 | , fHelper(helperArgs, aaType) |
490 | , fPrimitiveType(primitiveType) |
491 | , fMultipleViewMatrices(false) |
492 | , fColorSpaceXform(std::move(colorSpaceXform)) { |
493 | SkASSERT(vertices); |
494 | |
495 | SkVerticesPriv info(vertices->priv()); |
496 | |
497 | fVertexCount = info.vertexCount(); |
498 | fIndexCount = info.indexCount(); |
499 | fColorArrayType = info.hasColors() ? ColorArrayType::kSkColor |
500 | : ColorArrayType::kUnused; |
501 | fLocalCoordsType = info.hasTexCoords() ? LocalCoordsType::kExplicit |
502 | : LocalCoordsType::kUsePosition; |
503 | if (localToWorld) { |
504 | fLocalToWorld = *localToWorld; |
505 | } else { |
506 | fLocalToWorld.setIdentity(); |
507 | } |
508 | |
509 | Mesh& mesh = fMeshes.push_back(); |
510 | mesh.fColor = color; |
511 | mesh.fViewMatrix = viewMatrix; |
512 | mesh.fVertices = std::move(vertices); |
513 | mesh.fIgnoreColors = false; |
514 | |
515 | IsHairline zeroArea; |
516 | if (GrIsPrimTypeLines(primitiveType) || GrPrimitiveType::kPoints == primitiveType) { |
517 | zeroArea = IsHairline::kYes; |
518 | } else { |
519 | zeroArea = IsHairline::kNo; |
520 | } |
521 | |
522 | this->setTransformedBounds(mesh.fVertices->bounds(), |
523 | mesh.fViewMatrix, |
524 | HasAABloat::kNo, |
525 | zeroArea); |
526 | } |
527 | |
528 | #ifdef SK_DEBUG |
529 | SkString DrawVerticesOp::dumpInfo() const { |
530 | SkString string; |
531 | string.appendf("PrimType: %d, MeshCount %d, VCount: %d, ICount: %d\n" , (int)fPrimitiveType, |
532 | fMeshes.count(), fVertexCount, fIndexCount); |
533 | string += fHelper.dumpInfo(); |
534 | string += INHERITED::dumpInfo(); |
535 | return string; |
536 | } |
537 | #endif |
538 | |
539 | GrDrawOp::FixedFunctionFlags DrawVerticesOp::fixedFunctionFlags() const { |
540 | return fHelper.fixedFunctionFlags(); |
541 | } |
542 | |
543 | GrProcessorSet::Analysis DrawVerticesOp::finalize( |
544 | const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage, |
545 | GrClampType clampType) { |
546 | GrProcessorAnalysisColor gpColor; |
547 | if (this->requiresPerVertexColors()) { |
548 | gpColor.setToUnknown(); |
549 | } else { |
550 | gpColor.setToConstant(fMeshes.front().fColor); |
551 | } |
552 | auto result = fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType, |
553 | GrProcessorAnalysisCoverage::kNone, &gpColor); |
554 | if (gpColor.isConstant(&fMeshes.front().fColor)) { |
555 | fMeshes.front().fIgnoreColors = true; |
556 | fColorArrayType = ColorArrayType::kUnused; |
557 | } |
558 | if (!fHelper.usesLocalCoords()) { |
559 | fLocalCoordsType = LocalCoordsType::kUnused; |
560 | } |
561 | return result; |
562 | } |
563 | |
564 | GrGeometryProcessor* DrawVerticesOp::makeGP(SkArenaAlloc* arena) { |
565 | const SkMatrix& vm = fMultipleViewMatrices ? SkMatrix::I() : fMeshes[0].fViewMatrix; |
566 | |
567 | SkVerticesPriv info(fMeshes[0].fVertices->priv()); |
568 | |
569 | sk_sp<GrColorSpaceXform> csxform = (fColorArrayType == ColorArrayType::kSkColor || |
570 | info.hasUsage(SkVertices::Attribute::Usage::kColor)) |
571 | ? fColorSpaceXform |
572 | : nullptr; |
573 | |
574 | auto gp = VerticesGP::Make(arena, fLocalCoordsType, fColorArrayType, fMeshes[0].fColor, |
575 | std::move(csxform), vm, info.attributes(), info.attributeCount(), |
576 | fLocalToWorld); |
577 | SkASSERT(this->vertexStride() == gp->vertexStride()); |
578 | return gp; |
579 | } |
580 | |
581 | void DrawVerticesOp::onCreateProgramInfo(const GrCaps* caps, |
582 | SkArenaAlloc* arena, |
583 | const GrSurfaceProxyView* writeView, |
584 | GrAppliedClip&& appliedClip, |
585 | const GrXferProcessor::DstProxyView& dstProxyView) { |
586 | GrGeometryProcessor* gp = this->makeGP(arena); |
587 | fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip), |
588 | dstProxyView, gp, this->primitiveType()); |
589 | } |
590 | |
591 | void DrawVerticesOp::onPrepareDraws(Target* target) { |
592 | // Allocate buffers. |
593 | size_t vertexStride = this->vertexStride(); |
594 | sk_sp<const GrBuffer> vertexBuffer; |
595 | int firstVertex = 0; |
596 | GrVertexWriter verts{ |
597 | target->makeVertexSpace(vertexStride, fVertexCount, &vertexBuffer, &firstVertex)}; |
598 | if (!verts.fPtr) { |
599 | SkDebugf("Could not allocate vertices\n" ); |
600 | return; |
601 | } |
602 | |
603 | sk_sp<const GrBuffer> indexBuffer; |
604 | int firstIndex = 0; |
605 | uint16_t* indices = nullptr; |
606 | if (this->isIndexed()) { |
607 | indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex); |
608 | if (!indices) { |
609 | SkDebugf("Could not allocate indices\n" ); |
610 | return; |
611 | } |
612 | } |
613 | |
614 | // Copy data into the buffers. |
615 | bool hasColorAttribute = this->requiresPerVertexColors(); |
616 | bool hasLocalCoordsAttribute = this->requiresPerVertexLocalCoords(); |
617 | int vertexOffset = 0; |
618 | |
619 | for (const auto& mesh : fMeshes) { |
620 | SkVerticesPriv info(mesh.fVertices->priv()); |
621 | |
622 | // Copy data into the index buffer. |
623 | if (indices) { |
624 | int indexCount = info.indexCount(); |
625 | for (int i = 0; i < indexCount; ++i) { |
626 | *indices++ = info.indices()[i] + vertexOffset; |
627 | } |
628 | } |
629 | |
630 | // Copy data into the vertex buffer. |
631 | int vertexCount = info.vertexCount(); |
632 | const SkPoint* positions = info.positions(); |
633 | const SkColor* colors = info.colors(); |
634 | const SkPoint* localCoords = info.texCoords() ? info.texCoords() : positions; |
635 | const void* custom = info.customData(); |
636 | size_t customDataSize = info.customDataSize(); |
637 | |
638 | // TODO4F: Preserve float colors |
639 | GrColor meshColor = mesh.fColor.toBytes_RGBA(); |
640 | |
641 | SkPoint* posBase = (SkPoint*)verts.fPtr; |
642 | |
643 | for (int i = 0; i < vertexCount; ++i) { |
644 | verts.write(positions[i]); |
645 | if (hasColorAttribute) { |
646 | verts.write(mesh.hasPerVertexColors() ? colors[i] : meshColor); |
647 | } |
648 | if (hasLocalCoordsAttribute) { |
649 | verts.write(localCoords[i]); |
650 | } |
651 | if (customDataSize) { |
652 | verts.writeRaw(custom, customDataSize); |
653 | custom = SkTAddOffset<const void>(custom, customDataSize); |
654 | } |
655 | } |
656 | |
657 | if (fMultipleViewMatrices) { |
658 | SkASSERT(!mesh.fViewMatrix.hasPerspective()); |
659 | SkMatrixPriv::MapPointsWithStride(mesh.fViewMatrix, posBase, vertexStride, vertexCount); |
660 | } |
661 | |
662 | vertexOffset += vertexCount; |
663 | } |
664 | |
665 | SkASSERT(!fMesh); |
666 | fMesh = target->allocMesh(); |
667 | if (this->isIndexed()) { |
668 | fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertexCount - 1, |
669 | GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex); |
670 | } else { |
671 | fMesh->set(std::move(vertexBuffer), fVertexCount, firstVertex); |
672 | } |
673 | } |
674 | |
675 | void DrawVerticesOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) { |
676 | if (!fProgramInfo) { |
677 | this->createProgramInfo(flushState); |
678 | } |
679 | |
680 | if (!fProgramInfo || !fMesh) { |
681 | return; |
682 | } |
683 | |
684 | flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds); |
685 | flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline()); |
686 | flushState->drawMesh(*fMesh); |
687 | } |
688 | |
689 | GrOp::CombineResult DrawVerticesOp::onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*, |
690 | const GrCaps& caps) { |
691 | DrawVerticesOp* that = t->cast<DrawVerticesOp>(); |
692 | |
693 | if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) { |
694 | return CombineResult::kCannotCombine; |
695 | } |
696 | |
697 | if (!this->combinablePrimitive() || this->primitiveType() != that->primitiveType()) { |
698 | return CombineResult::kCannotCombine; |
699 | } |
700 | |
701 | if (this->isIndexed() != that->isIndexed()) { |
702 | return CombineResult::kCannotCombine; |
703 | } |
704 | |
705 | if (fVertexCount + that->fVertexCount > SkTo<int>(UINT16_MAX)) { |
706 | return CombineResult::kCannotCombine; |
707 | } |
708 | |
709 | SkVerticesPriv vThis(this->fMeshes[0].fVertices->priv()), |
710 | vThat(that->fMeshes[0].fVertices->priv()); |
711 | if (vThis.attributeCount() != vThat.attributeCount() || |
712 | !std::equal(vThis.attributes(), vThis.attributes() + vThis.attributeCount(), |
713 | vThat.attributes())) { |
714 | return CombineResult::kCannotCombine; |
715 | } |
716 | |
717 | if (vThis.usesLocalToWorldMatrix() && this->fLocalToWorld != that->fLocalToWorld) { |
718 | return CombineResult::kCannotCombine; |
719 | } |
720 | |
721 | // We can't mix draws that use SkColor vertex colors with those that don't. We can mix uniform |
722 | // color draws with GrColor draws (by expanding the uniform color into vertex color). |
723 | if ((fColorArrayType == ColorArrayType::kSkColor) != |
724 | (that->fColorArrayType == ColorArrayType::kSkColor)) { |
725 | return CombineResult::kCannotCombine; |
726 | } |
727 | |
728 | // If we're acquiring a mesh with a different view matrix, or an op that needed multiple view |
729 | // matrices, we need multiple view matrices. |
730 | bool needMultipleViewMatrices = |
731 | fMultipleViewMatrices || that->fMultipleViewMatrices || |
732 | !SkMatrixPriv::CheapEqual(this->fMeshes[0].fViewMatrix, that->fMeshes[0].fViewMatrix); |
733 | |
734 | // ... but we can't enable multiple view matrices if any of them have perspective, or our other |
735 | // varyings won't be interpolated correctly. |
736 | if (needMultipleViewMatrices && (this->fMeshes[0].fViewMatrix.hasPerspective() || |
737 | that->fMeshes[0].fViewMatrix.hasPerspective())) { |
738 | return CombineResult::kCannotCombine; |
739 | } else { |
740 | fMultipleViewMatrices = needMultipleViewMatrices; |
741 | } |
742 | |
743 | // If the other op already required per-vertex colors, the combined mesh does. |
744 | if (that->fColorArrayType == ColorArrayType::kPremulGrColor) { |
745 | fColorArrayType = ColorArrayType::kPremulGrColor; |
746 | } |
747 | |
748 | // If we combine meshes with different (uniform) colors, switch to per-vertex colors. |
749 | if (fColorArrayType == ColorArrayType::kUnused) { |
750 | SkASSERT(that->fColorArrayType == ColorArrayType::kUnused); |
751 | if (this->fMeshes[0].fColor != that->fMeshes[0].fColor) { |
752 | fColorArrayType = ColorArrayType::kPremulGrColor; |
753 | } |
754 | } |
755 | |
756 | // NOTE: For SkColor vertex colors, the source color space is always sRGB, and the destination |
757 | // gamut is determined by the render target context. A mis-match should be impossible. |
758 | SkASSERT(GrColorSpaceXform::Equals(fColorSpaceXform.get(), that->fColorSpaceXform.get())); |
759 | |
760 | // If the other op already required explicit local coords the combined mesh does. |
761 | if (that->fLocalCoordsType == LocalCoordsType::kExplicit) { |
762 | fLocalCoordsType = LocalCoordsType::kExplicit; |
763 | } |
764 | |
765 | // If we were planning to use positions for local coords but now have multiple view matrices, |
766 | // switch to explicit local coords. |
767 | if (fLocalCoordsType == LocalCoordsType::kUsePosition && fMultipleViewMatrices) { |
768 | fLocalCoordsType = LocalCoordsType::kExplicit; |
769 | } |
770 | |
771 | fMeshes.push_back_n(that->fMeshes.count(), that->fMeshes.begin()); |
772 | fVertexCount += that->fVertexCount; |
773 | fIndexCount += that->fIndexCount; |
774 | |
775 | return CombineResult::kMerged; |
776 | } |
777 | |
778 | } // anonymous namespace |
779 | |
780 | static GrPrimitiveType SkVertexModeToGrPrimitiveType(SkVertices::VertexMode mode) { |
781 | switch (mode) { |
782 | case SkVertices::kTriangles_VertexMode: |
783 | return GrPrimitiveType::kTriangles; |
784 | case SkVertices::kTriangleStrip_VertexMode: |
785 | return GrPrimitiveType::kTriangleStrip; |
786 | case SkVertices::kTriangleFan_VertexMode: |
787 | break; |
788 | } |
789 | SK_ABORT("Invalid mode" ); |
790 | } |
791 | |
792 | std::unique_ptr<GrDrawOp> GrDrawVerticesOp::Make(GrRecordingContext* context, |
793 | GrPaint&& paint, |
794 | sk_sp<SkVertices> vertices, |
795 | const SkMatrix& viewMatrix, |
796 | GrAAType aaType, |
797 | sk_sp<GrColorSpaceXform> colorSpaceXform, |
798 | GrPrimitiveType* overridePrimType, |
799 | const SkRuntimeEffect* effect, |
800 | const SkM44* localToWorld) { |
801 | SkASSERT(vertices); |
802 | GrPrimitiveType primType = overridePrimType |
803 | ? *overridePrimType |
804 | : SkVertexModeToGrPrimitiveType(vertices->priv().mode()); |
805 | return GrSimpleMeshDrawOpHelper::FactoryHelper<DrawVerticesOp>( |
806 | context, std::move(paint), std::move(vertices), primType, aaType, |
807 | std::move(colorSpaceXform), viewMatrix, effect, localToWorld); |
808 | } |
809 | |
810 | /////////////////////////////////////////////////////////////////////////////////////////////////// |
811 | |
812 | #if GR_TEST_UTILS |
813 | |
814 | #include "src/gpu/GrDrawOpTest.h" |
815 | |
816 | static uint32_t seed_vertices(GrPrimitiveType type) { |
817 | switch (type) { |
818 | case GrPrimitiveType::kTriangles: |
819 | case GrPrimitiveType::kTriangleStrip: |
820 | return 3; |
821 | case GrPrimitiveType::kPoints: |
822 | return 1; |
823 | case GrPrimitiveType::kLines: |
824 | case GrPrimitiveType::kLineStrip: |
825 | return 2; |
826 | case GrPrimitiveType::kPatches: |
827 | case GrPrimitiveType::kPath: |
828 | SkASSERT(0); |
829 | return 0; |
830 | } |
831 | SK_ABORT("Incomplete switch\n" ); |
832 | } |
833 | |
834 | static uint32_t primitive_vertices(GrPrimitiveType type) { |
835 | switch (type) { |
836 | case GrPrimitiveType::kTriangles: |
837 | return 3; |
838 | case GrPrimitiveType::kLines: |
839 | return 2; |
840 | case GrPrimitiveType::kTriangleStrip: |
841 | case GrPrimitiveType::kPoints: |
842 | case GrPrimitiveType::kLineStrip: |
843 | return 1; |
844 | case GrPrimitiveType::kPatches: |
845 | case GrPrimitiveType::kPath: |
846 | SkASSERT(0); |
847 | return 0; |
848 | } |
849 | SK_ABORT("Incomplete switch\n" ); |
850 | } |
851 | |
852 | static SkPoint random_point(SkRandom* random, SkScalar min, SkScalar max) { |
853 | SkPoint p; |
854 | p.fX = random->nextRangeScalar(min, max); |
855 | p.fY = random->nextRangeScalar(min, max); |
856 | return p; |
857 | } |
858 | |
859 | static void randomize_params(size_t count, size_t maxVertex, SkScalar min, SkScalar max, |
860 | SkRandom* random, SkTArray<SkPoint>* positions, |
861 | SkTArray<SkPoint>* texCoords, bool hasTexCoords, |
862 | SkTArray<uint32_t>* colors, bool hasColors, |
863 | SkTArray<uint16_t>* indices, bool hasIndices) { |
864 | for (uint32_t v = 0; v < count; v++) { |
865 | positions->push_back(random_point(random, min, max)); |
866 | if (hasTexCoords) { |
867 | texCoords->push_back(random_point(random, min, max)); |
868 | } |
869 | if (hasColors) { |
870 | colors->push_back(GrRandomColor(random)); |
871 | } |
872 | if (hasIndices) { |
873 | SkASSERT(maxVertex <= UINT16_MAX); |
874 | indices->push_back(random->nextULessThan((uint16_t)maxVertex)); |
875 | } |
876 | } |
877 | } |
878 | |
879 | GR_DRAW_OP_TEST_DEFINE(DrawVerticesOp) { |
880 | GrPrimitiveType types[] = { |
881 | GrPrimitiveType::kTriangles, |
882 | GrPrimitiveType::kTriangleStrip, |
883 | GrPrimitiveType::kPoints, |
884 | GrPrimitiveType::kLines, |
885 | GrPrimitiveType::kLineStrip |
886 | }; |
887 | auto type = types[random->nextULessThan(SK_ARRAY_COUNT(types))]; |
888 | |
889 | uint32_t primitiveCount = random->nextRangeU(1, 100); |
890 | |
891 | // TODO make 'sensible' indexbuffers |
892 | SkTArray<SkPoint> positions; |
893 | SkTArray<SkPoint> texCoords; |
894 | SkTArray<uint32_t> colors; |
895 | SkTArray<uint16_t> indices; |
896 | |
897 | bool hasTexCoords = random->nextBool(); |
898 | bool hasIndices = random->nextBool(); |
899 | bool hasColors = random->nextBool(); |
900 | |
901 | uint32_t vertexCount = seed_vertices(type) + (primitiveCount - 1) * primitive_vertices(type); |
902 | |
903 | static const SkScalar kMinVertExtent = -100.f; |
904 | static const SkScalar kMaxVertExtent = 100.f; |
905 | randomize_params(seed_vertices(type), vertexCount, kMinVertExtent, kMaxVertExtent, random, |
906 | &positions, &texCoords, hasTexCoords, &colors, hasColors, &indices, |
907 | hasIndices); |
908 | |
909 | for (uint32_t i = 1; i < primitiveCount; i++) { |
910 | randomize_params(primitive_vertices(type), vertexCount, kMinVertExtent, kMaxVertExtent, |
911 | random, &positions, &texCoords, hasTexCoords, &colors, hasColors, &indices, |
912 | hasIndices); |
913 | } |
914 | |
915 | SkMatrix viewMatrix = GrTest::TestMatrix(random); |
916 | |
917 | sk_sp<GrColorSpaceXform> colorSpaceXform = GrTest::TestColorXform(random); |
918 | |
919 | static constexpr SkVertices::VertexMode kIgnoredMode = SkVertices::kTriangles_VertexMode; |
920 | sk_sp<SkVertices> vertices = SkVertices::MakeCopy(kIgnoredMode, vertexCount, positions.begin(), |
921 | texCoords.begin(), colors.begin(), |
922 | hasIndices ? indices.count() : 0, |
923 | indices.begin()); |
924 | GrAAType aaType = GrAAType::kNone; |
925 | if (numSamples > 1 && random->nextBool()) { |
926 | aaType = GrAAType::kMSAA; |
927 | } |
928 | return GrDrawVerticesOp::Make(context, std::move(paint), std::move(vertices), viewMatrix, |
929 | aaType, std::move(colorSpaceXform), &type, nullptr, nullptr); |
930 | } |
931 | |
932 | #endif |
933 | |