1 /*
2 * Copyright 2021 Google LLC
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 "src/gpu/ganesh/ops/DrawMeshOp.h"
9
10 #include "include/core/SkData.h"
11 #include "include/core/SkMesh.h"
12 #include "src/base/SkArenaAlloc.h"
13 #include "src/core/SkMeshPriv.h"
14 #include "src/core/SkRuntimeEffectPriv.h"
15 #include "src/core/SkVerticesPriv.h"
16 #include "src/gpu/BufferWriter.h"
17 #include "src/gpu/KeyBuilder.h"
18 #include "src/gpu/ganesh/GrGeometryProcessor.h"
19 #include "src/gpu/ganesh/GrMeshBuffers.h"
20 #include "src/gpu/ganesh/GrOpFlushState.h"
21 #include "src/gpu/ganesh/GrProgramInfo.h"
22 #include "src/gpu/ganesh/effects/GrTextureEffect.h"
23 #include "src/gpu/ganesh/glsl/GrGLSLColorSpaceXformHelper.h"
24 #include "src/gpu/ganesh/glsl/GrGLSLFragmentShaderBuilder.h"
25 #include "src/gpu/ganesh/glsl/GrGLSLProgramBuilder.h"
26 #include "src/gpu/ganesh/glsl/GrGLSLVarying.h"
27 #include "src/gpu/ganesh/glsl/GrGLSLVertexGeoBuilder.h"
28 #include "src/gpu/ganesh/ops/GrSimpleMeshDrawOpHelper.h"
29 #include "src/sksl/codegen/SkSLPipelineStageCodeGenerator.h"
30 #include "src/sksl/ir/SkSLProgram.h"
31 #include "src/sksl/ir/SkSLVarDeclarations.h"
32
33 using namespace skia_private;
34
35 namespace {
36
primitive_type(SkMesh::Mode mode)37 GrPrimitiveType primitive_type(SkMesh::Mode mode) {
38 switch (mode) {
39 case SkMesh::Mode::kTriangles: return GrPrimitiveType::kTriangles;
40 case SkMesh::Mode::kTriangleStrip: return GrPrimitiveType::kTriangleStrip;
41 }
42 SkUNREACHABLE;
43 }
44
45 using MeshAttributeType = SkMeshSpecification::Attribute::Type;
46
attrib_type(MeshAttributeType type)47 GrVertexAttribType attrib_type(MeshAttributeType type) {
48 switch (type) {
49 case MeshAttributeType::kFloat: return kFloat_GrVertexAttribType;
50 case MeshAttributeType::kFloat2: return kFloat2_GrVertexAttribType;
51 case MeshAttributeType::kFloat3: return kFloat3_GrVertexAttribType;
52 case MeshAttributeType::kFloat4: return kFloat4_GrVertexAttribType;
53 case MeshAttributeType::kUByte4_unorm: return kUByte4_norm_GrVertexAttribType;
54 }
55 SkUNREACHABLE;
56 }
57
58 class MeshGP : public GrGeometryProcessor {
59 private:
60 using ChildPtr = SkRuntimeEffect::ChildPtr;
61
62 public:
Make(SkArenaAlloc * arena,sk_sp<SkMeshSpecification> spec,sk_sp<GrColorSpaceXform> colorSpaceXform,const SkMatrix & viewMatrix,const std::optional<SkPMColor4f> & color,bool needsLocalCoords,sk_sp<const SkData> uniforms,SkSpan<std::unique_ptr<GrFragmentProcessor>> children)63 static GrGeometryProcessor* Make(
64 SkArenaAlloc* arena,
65 sk_sp<SkMeshSpecification> spec,
66 sk_sp<GrColorSpaceXform> colorSpaceXform,
67 const SkMatrix& viewMatrix,
68 const std::optional<SkPMColor4f>& color,
69 bool needsLocalCoords,
70 sk_sp<const SkData> uniforms,
71 SkSpan<std::unique_ptr<GrFragmentProcessor>> children) {
72 return arena->make([&](void* ptr) {
73 return new (ptr) MeshGP(std::move(spec),
74 std::move(colorSpaceXform),
75 viewMatrix,
76 std::move(color),
77 needsLocalCoords,
78 std::move(uniforms),
79 children);
80 });
81 }
82
name() const83 const char* name() const override { return "MeshGP"; }
84
addToKey(const GrShaderCaps & caps,skgpu::KeyBuilder * b) const85 void addToKey(const GrShaderCaps& caps, skgpu::KeyBuilder* b) const override {
86 b->add32(SkMeshSpecificationPriv::Hash(*fSpec), "custom mesh spec hash");
87 b->add32(ProgramImpl::ComputeMatrixKey(caps, fViewMatrix), "view matrix key");
88 if (SkMeshSpecificationPriv::GetColorType(*fSpec) !=
89 SkMeshSpecificationPriv::ColorType::kNone) {
90 b->add32(GrColorSpaceXform::XformKey(fColorSpaceXform.get()), "colorspace xform key");
91 }
92 for (const std::unique_ptr<GrFragmentProcessor>& fp : fChildren) {
93 if (fp) {
94 fp->addToKey(caps, b);
95 } else {
96 b->addBool(false, "null effect");
97 }
98 }
99 }
100
makeProgramImpl(const GrShaderCaps &) const101 std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override {
102 return std::make_unique<Impl>();
103 }
104
105 private:
106 class Impl : public ProgramImpl {
107 public:
setData(const GrGLSLProgramDataManager & pdman,const GrShaderCaps & shaderCaps,const GrGeometryProcessor & geomProc)108 void setData(const GrGLSLProgramDataManager& pdman,
109 const GrShaderCaps& shaderCaps,
110 const GrGeometryProcessor& geomProc) override {
111 const auto& mgp = geomProc.cast<MeshGP>();
112 SetTransform(pdman, shaderCaps, fViewMatrixUniform, mgp.fViewMatrix, &fViewMatrix);
113 // Set up uniforms for the color-space transform.
114 fColorSpaceHelper.setData(pdman, mgp.fColorSpaceXform.get());
115 // Assign the paint color to a uniform.
116 if (fColorUniform.isValid()) {
117 pdman.set4fv(fColorUniform, 1, mgp.fColor.vec());
118 }
119 // Update uniforms associated with the mesh vertex/fragment program.
120 if (mgp.fUniforms) {
121 pdman.setRuntimeEffectUniforms(mgp.fSpec->uniforms(),
122 SkSpan(fSpecUniformHandles),
123 mgp.fUniforms->data());
124 }
125 // Recursively update uniforms associated with the mesh child FPs.
126 for (size_t index = 0; index < mgp.fChildren.size(); ++index) {
127 if (const GrFragmentProcessor* fp = mgp.fChildren[index].get()) {
128 GrFragmentProcessor::ProgramImpl* impl = fChildImpls[index].get();
129 SkASSERT(impl);
130
131 fp->visitWithImpls([&](const GrFragmentProcessor& fp,
132 GrFragmentProcessor::ProgramImpl& impl) {
133 impl.setData(pdman, fp);
134 },
135 *impl);
136 }
137 }
138 }
139
140 private:
141 class MeshCallbacks : public SkSL::PipelineStage::Callbacks {
142 public:
MeshCallbacks(Impl * self,const MeshGP & gp,GrGLSLShaderBuilder * builder,GrGLSLUniformHandler * uniformHandler,const char * mainName,const SkSL::Context & context)143 MeshCallbacks(Impl* self,
144 const MeshGP& gp,
145 GrGLSLShaderBuilder* builder,
146 GrGLSLUniformHandler* uniformHandler,
147 const char* mainName,
148 const SkSL::Context& context)
149 : fSelf(self)
150 , fGP(gp)
151 , fBuilder(builder)
152 , fUniformHandler(uniformHandler)
153 , fMainName(mainName)
154 , fContext(context) {}
155
declareUniform(const SkSL::VarDeclaration * decl)156 std::string declareUniform(const SkSL::VarDeclaration* decl) override {
157 const SkSL::Variable* var = decl->var();
158 if (var->type().isOpaque()) {
159 // Nothing to do. The only opaque types we should see are children, and those
160 // will be handled in the `sample` overloads below.
161 SkASSERT(var->type().isEffectChild());
162 return std::string(var->name());
163 }
164
165 const SkSL::Type* type = &var->type();
166 bool isArray = false;
167 if (type->isArray()) {
168 type = &type->componentType();
169 isArray = true;
170 }
171
172 SkSLType gpuType;
173 SkAssertResult(SkSL::type_to_sksltype(fContext, *type, &gpuType));
174
175 SkString name(var->name());
176 const SkSpan<const SkMeshSpecification::Uniform> uniforms = fGP.fSpec->uniforms();
177 auto it = std::find_if(uniforms.begin(),
178 uniforms.end(),
179 [&name](SkMeshSpecification::Uniform uniform) {
180 return uniform.name == std::string_view(name.c_str(), name.size());
181 });
182 SkASSERT(it != uniforms.end());
183
184 size_t handleIdx = std::distance(uniforms.begin(), it);
185 UniformHandle* handle = &fSelf->fSpecUniformHandles[handleIdx];
186 if (handle->isValid()) {
187 const GrShaderVar& uniformVar = fUniformHandler->getUniformVariable(*handle);
188 return std::string(uniformVar.getName().c_str());
189 }
190
191 const SkMeshSpecification::Uniform& uniform = *it;
192 GrShaderFlags shaderFlags = kNone_GrShaderFlags;
193 if (uniform.flags & SkMeshSpecification::Uniform::Flags::kVertex_Flag) {
194 shaderFlags |= kVertex_GrShaderFlag;
195 }
196 if (uniform.flags & SkMeshSpecification::Uniform::Flags::kFragment_Flag) {
197 shaderFlags |= kFragment_GrShaderFlag;
198 }
199 SkASSERT(shaderFlags != kNone_GrShaderFlags);
200
201 const char* mangledName = nullptr;
202 *handle = fUniformHandler->addUniformArray(&fGP,
203 shaderFlags,
204 gpuType,
205 name.c_str(),
206 isArray ? var->type().columns() : 0,
207 &mangledName);
208 return std::string(mangledName);
209 }
210
getMangledName(const char * name)211 std::string getMangledName(const char* name) override {
212 return std::string(fBuilder->getMangledFunctionName(name).c_str());
213 }
214
getMainName()215 std::string getMainName() override { return fMainName; }
216
defineFunction(const char * decl,const char * body,bool isMain)217 void defineFunction(const char* decl, const char* body, bool isMain) override {
218 fBuilder->emitFunction(decl, body);
219 }
220
declareFunction(const char * decl)221 void declareFunction(const char* decl) override {
222 fBuilder->emitFunctionPrototype(decl);
223 }
224
defineStruct(const char * definition)225 void defineStruct(const char* definition) override {
226 fBuilder->definitionAppend(definition);
227 }
228
declareGlobal(const char * declaration)229 void declareGlobal(const char* declaration) override {
230 fBuilder->definitionAppend(declaration);
231 }
232
sampleShader(int index,std::string coords)233 std::string sampleShader(int index, std::string coords) override {
234 const GrFragmentProcessor* fp = fGP.fChildren[index].get();
235 if (!fp) {
236 // For a null shader, return transparent black.
237 return "half4(0)";
238 }
239 GrFragmentProcessor::ProgramImpl* impl = fSelf->fChildImpls[index].get();
240 SkASSERT(impl);
241 return fBuilder->getProgramBuilder()->invokeFP(*fp,
242 *impl,
243 /*inputColor=*/"half4(0)",
244 /*destColor=*/"half4(1)",
245 coords.c_str());
246 }
247
sampleColorFilter(int index,std::string color)248 std::string sampleColorFilter(int index, std::string color) override {
249 const GrFragmentProcessor* fp = fGP.fChildren[index].get();
250 if (!fp) {
251 // For a null color filter, return the color as-is.
252 return color;
253 }
254 GrFragmentProcessor::ProgramImpl* impl = fSelf->fChildImpls[index].get();
255 SkASSERT(impl);
256 return fBuilder->getProgramBuilder()->invokeFP(*fp,
257 *impl,
258 color.c_str(),
259 /*destColor=*/"half4(1)",
260 /*coords=*/"float2(0)");
261 }
262
sampleBlender(int index,std::string src,std::string dst)263 std::string sampleBlender(int index, std::string src, std::string dst) override {
264 const GrFragmentProcessor* fp = fGP.fChildren[index].get();
265 if (!fp) {
266 // For a null blend, perform src-over.
267 return SkSL::String::printf("blend_src_over(%s, %s)", src.c_str(), dst.c_str());
268 }
269 GrFragmentProcessor::ProgramImpl* impl = fSelf->fChildImpls[index].get();
270 SkASSERT(impl);
271 return fBuilder->getProgramBuilder()->invokeFP(*fp,
272 *impl,
273 src.c_str(),
274 dst.c_str(),
275 /*coords=*/"float2(0)");
276 }
277
toLinearSrgb(std::string color)278 std::string toLinearSrgb(std::string color) override {
279 SK_ABORT("Color transform intrinsics not allowed.");
280 }
281
fromLinearSrgb(std::string Color)282 std::string fromLinearSrgb(std::string Color) override {
283 SK_ABORT("Color transform intrinsics not allowed.");
284 }
285
286 Impl* fSelf;
287 const MeshGP& fGP;
288 GrGLSLShaderBuilder* fBuilder;
289 GrGLSLUniformHandler* fUniformHandler;
290 const char* fMainName;
291 const SkSL::Context& fContext;
292 };
293
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)294 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
295 const MeshGP& mgp = args.fGeomProc.cast<MeshGP>();
296 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
297 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
298 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
299 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
300 SkSpan<std::unique_ptr<GrFragmentProcessor>> children = mgp.fChildren;
301
302 // Create Impls for any child fragment processors.
303 fChildImpls.reserve_exact(children.size());
304 for (const std::unique_ptr<GrFragmentProcessor>& fp : children) {
305 fChildImpls.push_back(fp ? fp->makeProgramImpl() : nullptr);
306 }
307
308 SkASSERT(fSpecUniformHandles.empty());
309 fSpecUniformHandles.reserve_exact(mgp.fSpec->uniforms().size());
310 fSpecUniformHandles.push_back_n(mgp.fSpec->uniforms().size());
311
312 SkMeshSpecificationPriv::ColorType meshColorType =
313 SkMeshSpecificationPriv::GetColorType(*mgp.fSpec);
314 int passthroughLCVaryingIndex =
315 SkMeshSpecificationPriv::PassthroughLocalCoordsVaryingIndex(*mgp.fSpec);
316
317 // If the user's fragment shader doesn't output color and we also don't need its local
318 // coords then it isn't necessary to call it at all. We might not need its local coords
319 // because local coords aren't required for the paint or because we detected a
320 // passthrough varying returned from the user's FS.
321 bool needUserFS = (passthroughLCVaryingIndex < 0 && mgp.fNeedsLocalCoords) ||
322 meshColorType != SkMeshSpecificationPriv::ColorType::kNone;
323
324 if (!needUserFS && !mgp.fNeedsLocalCoords) {
325 // Don't bother with it if we don't need it.
326 passthroughLCVaryingIndex = -1;
327 }
328
329 SkSpan<const SkMeshSpecification::Varying> specVaryings =
330 SkMeshSpecificationPriv::Varyings(*mgp.fSpec);
331
332 ////// VS
333
334 // emit attributes
335 varyingHandler->emitAttributes(mgp);
336
337 // Define the user's vert function.
338 SkString userVertName = vertBuilder->getMangledFunctionName("custom_mesh_vs");
339 const SkSL::Program* customVS = SkMeshSpecificationPriv::VS(*mgp.fSpec);
340 MeshCallbacks vsCallbacks(this,
341 mgp,
342 vertBuilder,
343 uniformHandler,
344 userVertName.c_str(),
345 *customVS->fContext);
346 SkSL::PipelineStage::ConvertProgram(*customVS,
347 /*sampleCoords=*/"",
348 /*inputColor=*/"",
349 /*destColor=*/"",
350 &vsCallbacks);
351
352 // Copy the individual attributes into a struct
353 vertBuilder->codeAppendf("%s attributes;",
354 vsCallbacks.getMangledName("Attributes").c_str());
355 {
356 size_t i = 0;
357 SkASSERT(mgp.vertexAttributes().count() == (int)mgp.fSpec->attributes().size());
358 for (auto attr : mgp.vertexAttributes()) {
359 vertBuilder->codeAppendf("attributes.%s = %s;",
360 mgp.fSpec->attributes()[i++].name.c_str(),
361 attr.name());
362 }
363 }
364
365 // Call the user's vert function.
366 vertBuilder->codeAppendf("%s varyings = %s(attributes);",
367 vsCallbacks.getMangledName("Varyings").c_str(),
368 userVertName.c_str());
369
370 if (passthroughLCVaryingIndex >= 0 &&
371 SkMeshSpecificationPriv::VaryingIsDead(*mgp.fSpec, passthroughLCVaryingIndex)) {
372 vertBuilder->codeAppendf("float2 local = varyings.%s\n;",
373 specVaryings[passthroughLCVaryingIndex].name.c_str());
374 gpArgs->fLocalCoordVar = GrShaderVar("local", SkSLType::kFloat2);
375 gpArgs->fLocalCoordShader = kVertex_GrShaderType;
376 }
377
378 // Unpack the "varyings" from the struct into individual real varyings if they are
379 // required.
380 struct RealVarying {
381 size_t specIndex;
382 GrGLSLVarying varying;
383 };
384 STArray<SkMeshSpecification::kMaxVaryings, RealVarying> realVaryings;
385 if (needUserFS) {
386 for (size_t i = 0; i < specVaryings.size(); ++i) {
387 const auto& v = specVaryings[i];
388 if (SkMeshSpecificationPriv::VaryingIsDead(*mgp.fSpec, i)) {
389 continue;
390 }
391 RealVarying rv {i, SkMeshSpecificationPriv::VaryingTypeAsSLType(v.type)};
392 realVaryings.push_back(rv);
393 varyingHandler->addVarying(v.name.c_str(), &realVaryings.back().varying);
394 vertBuilder->codeAppendf("%s = varyings.%s;",
395 realVaryings.back().varying.vsOut(),
396 v.name.c_str());
397 if (passthroughLCVaryingIndex == SkToInt(i)) {
398 SkASSERT(gpArgs->fLocalCoordVar.getType() == SkSLType::kVoid);
399 gpArgs->fLocalCoordVar = realVaryings.back().varying.vsOutVar();
400 gpArgs->fLocalCoordShader = kVertex_GrShaderType;
401 }
402 }
403 }
404
405 vertBuilder->codeAppend("float2 pos = varyings.position;");
406 // Setup position
407 WriteOutputPosition(vertBuilder,
408 uniformHandler,
409 *args.fShaderCaps,
410 gpArgs,
411 "pos",
412 mgp.fViewMatrix,
413 &fViewMatrixUniform);
414
415 ////// FS
416
417 int samplerIndex = 0;
418 for (size_t fpIdx = 0; fpIdx < mgp.fChildren.size(); ++fpIdx) {
419 if (const GrFragmentProcessor* fp = mgp.fChildren[fpIdx].get()) {
420 GrFragmentProcessor::ProgramImpl* impl = fChildImpls[fpIdx].get();
421 SkASSERT(impl);
422
423 // Hook up sampler handles to texture effects. This code needs to keep
424 // consistent with the code that up sampler handles (in the MeshGP ctor).
425 fp->visitWithImpls([&](const GrFragmentProcessor& fp,
426 GrFragmentProcessor::ProgramImpl& impl) {
427 if (fp.asTextureEffect()) {
428 static_cast<GrTextureEffect::Impl&>(impl).setSamplerHandle(
429 args.fTexSamplers[samplerIndex++]);
430 }
431 },
432 *impl);
433
434 // Write functions associated with this FP.
435 args.fFragBuilder->getProgramBuilder()->advanceStage();
436 args.fFragBuilder->getProgramBuilder()->writeFPFunction(*fp, *impl);
437 }
438 }
439
440 // Define the user's frag function.
441 fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
442 fragBuilder->codeAppendf("const half4 %s = half4(1);", args.fOutputCoverage);
443
444 SkString userFragName = fragBuilder->getMangledFunctionName("custom_mesh_fs");
445 const SkSL::Program* customFS = SkMeshSpecificationPriv::FS(*mgp.fSpec);
446 MeshCallbacks fsCallbacks(this,
447 mgp,
448 fragBuilder,
449 uniformHandler,
450 userFragName.c_str(),
451 *customFS->fContext);
452 SkSL::PipelineStage::ConvertProgram(*customFS,
453 /*sampleCoords=*/"",
454 /*inputColor=*/"",
455 /*destColor=*/"",
456 &fsCallbacks);
457 const char* uniformColorName = nullptr;
458 if (mgp.fColor != SK_PMColor4fILLEGAL) {
459 fColorUniform = uniformHandler->addUniform(nullptr,
460 kFragment_GrShaderFlag,
461 SkSLType::kHalf4,
462 "color",
463 &uniformColorName);
464 }
465 if (meshColorType == SkMeshSpecificationPriv::ColorType::kNone) {
466 SkASSERT(uniformColorName);
467 fragBuilder->codeAppendf("%s = %s;", args.fOutputColor, uniformColorName);
468 }
469
470 if (needUserFS) {
471 // Pack the real varyings into a struct to call the user's frag code.
472 fragBuilder->codeAppendf("%s varyings;",
473 fsCallbacks.getMangledName("Varyings").c_str());
474 for (const auto& rv : realVaryings) {
475 const auto& v = specVaryings[rv.specIndex];
476 fragBuilder->codeAppendf("varyings.%s = %s;",
477 v.name.c_str(),
478 rv.varying.vsOut());
479 }
480
481 // Grab the return local coords from the user's FS code only if we actually need it.
482 SkString local;
483 if (gpArgs->fLocalCoordVar.getType() == SkSLType::kVoid && mgp.fNeedsLocalCoords) {
484 gpArgs->fLocalCoordVar = GrShaderVar("local", SkSLType::kFloat2);
485 gpArgs->fLocalCoordShader = kFragment_GrShaderType;
486 local = "float2 local = ";
487 }
488 if (meshColorType == SkMeshSpecificationPriv::ColorType::kNone) {
489 fragBuilder->codeAppendf("%s%s(varyings);",
490 local.c_str(),
491 userFragName.c_str());
492 } else {
493 fColorSpaceHelper.emitCode(uniformHandler,
494 mgp.fColorSpaceXform.get(),
495 kFragment_GrShaderFlag);
496 if (meshColorType == SkMeshSpecificationPriv::ColorType::kFloat4) {
497 fragBuilder->codeAppendf("float4 color;");
498 } else {
499 SkASSERT(meshColorType == SkMeshSpecificationPriv::ColorType::kHalf4);
500 fragBuilder->codeAppendf("half4 color;");
501 }
502
503 fragBuilder->codeAppendf("%s%s(varyings, color);",
504 local.c_str(),
505 userFragName.c_str());
506 // We ignore the user's color if analysis told us to emit a specific color.
507 // The user color might be float4 and we expect a half4 in the colorspace
508 // helper.
509 const char* color = uniformColorName ? uniformColorName : "half4(color)";
510 SkString xformedColor;
511 fragBuilder->appendColorGamutXform(&xformedColor, color, &fColorSpaceHelper);
512 fragBuilder->codeAppendf("%s = %s;", args.fOutputColor, xformedColor.c_str());
513 }
514 }
515 SkASSERT(!mgp.fNeedsLocalCoords ||
516 gpArgs->fLocalCoordVar.getType() == SkSLType::kFloat2);
517 }
518
519 private:
520 SkMatrix fViewMatrix = SkMatrix::InvalidMatrix();
521
522 STArray<2, std::unique_ptr<GrFragmentProcessor::ProgramImpl>> fChildImpls;
523 UniformHandle fViewMatrixUniform;
524 UniformHandle fColorUniform;
525 STArray<8, UniformHandle> fSpecUniformHandles;
526
527 GrGLSLColorSpaceXformHelper fColorSpaceHelper;
528 };
529
MeshGP(sk_sp<SkMeshSpecification> spec,sk_sp<GrColorSpaceXform> colorSpaceXform,const SkMatrix & viewMatrix,const std::optional<SkPMColor4f> & color,bool needsLocalCoords,sk_sp<const SkData> uniforms,SkSpan<std::unique_ptr<GrFragmentProcessor>> children)530 MeshGP(sk_sp<SkMeshSpecification> spec,
531 sk_sp<GrColorSpaceXform> colorSpaceXform,
532 const SkMatrix& viewMatrix,
533 const std::optional<SkPMColor4f>& color,
534 bool needsLocalCoords,
535 sk_sp<const SkData> uniforms,
536 SkSpan<std::unique_ptr<GrFragmentProcessor>> children)
537 : INHERITED(kVerticesGP_ClassID)
538 , fSpec(std::move(spec))
539 , fUniforms(std::move(uniforms))
540 , fChildren(children)
541 , fViewMatrix(viewMatrix)
542 , fColorSpaceXform(std::move(colorSpaceXform))
543 , fNeedsLocalCoords(needsLocalCoords) {
544 fColor = color.value_or(SK_PMColor4fILLEGAL);
545 for (const auto& srcAttr : fSpec->attributes()) {
546 fAttributes.emplace_back(srcAttr.name.c_str(),
547 attrib_type(srcAttr.type),
548 SkMeshSpecificationPriv::AttrTypeAsSLType(srcAttr.type),
549 srcAttr.offset);
550 }
551 this->setVertexAttributes(fAttributes.data(), fAttributes.size(), fSpec->stride());
552
553 // We are relying here on the fact that `visitTextureEffects` and `visitWithImpls` walk the
554 // FP tree in the same order.
555 for (const std::unique_ptr<GrFragmentProcessor>& fp : fChildren) {
556 if (fp) {
557 fp->visitTextureEffects([&](const GrTextureEffect& te) {
558 fTextureSamplers.push_back({te.samplerState(),
559 te.view().proxy()->backendFormat(),
560 te.view().swizzle()});
561 });
562 }
563 }
564 this->setTextureSamplerCnt(fTextureSamplers.size());
565 }
566
onTextureSampler(int index) const567 const TextureSampler& onTextureSampler(int index) const override {
568 return fTextureSamplers[index];
569 }
570
571 sk_sp<SkMeshSpecification> fSpec;
572 sk_sp<const SkData> fUniforms;
573 SkSpan<std::unique_ptr<GrFragmentProcessor>> fChildren; // backed by a TArray in MeshOp
574 TArray<TextureSampler> fTextureSamplers;
575 std::vector<Attribute> fAttributes;
576 SkMatrix fViewMatrix;
577 SkPMColor4f fColor;
578 sk_sp<GrColorSpaceXform> fColorSpaceXform;
579 bool fNeedsLocalCoords;
580
581 using INHERITED = GrGeometryProcessor;
582 };
583
584 class MeshOp final : public GrMeshDrawOp {
585 private:
586 using Helper = GrSimpleMeshDrawOpHelper;
587 using ChildPtr = SkRuntimeEffect::ChildPtr;
588
589 public:
590 DEFINE_OP_CLASS_ID
591
592 MeshOp(GrProcessorSet*,
593 const SkPMColor4f&,
594 const SkMesh&,
595 TArray<std::unique_ptr<GrFragmentProcessor>> children,
596 GrAAType,
597 sk_sp<GrColorSpaceXform>,
598 const SkMatrix&);
599
600 MeshOp(GrProcessorSet*,
601 const SkPMColor4f&,
602 sk_sp<SkVertices>,
603 const GrPrimitiveType*,
604 GrAAType,
605 sk_sp<GrColorSpaceXform>,
606 const SkMatrix&);
607
name() const608 const char* name() const override { return "MeshOp"; }
609
visitProxies(const GrVisitProxyFunc & func) const610 void visitProxies(const GrVisitProxyFunc& func) const override {
611 for (const std::unique_ptr<GrFragmentProcessor>& fp : fChildren) {
612 if (fp) {
613 fp->visitTextureEffects([&](const GrTextureEffect& te) {
614 func(te.view().proxy(), te.view().mipmapped());
615 });
616 }
617 }
618 if (fProgramInfo) {
619 fProgramInfo->visitFPProxies(func);
620 } else {
621 fHelper.visitProxies(func);
622 }
623 }
624
625 FixedFunctionFlags fixedFunctionFlags() const override;
626
627 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, GrClampType) override;
628
629 private:
programInfo()630 GrProgramInfo* programInfo() override { return fProgramInfo; }
631
632 void onCreateProgramInfo(const GrCaps*,
633 SkArenaAlloc*,
634 const GrSurfaceProxyView& writeView,
635 bool usesMSAASurface,
636 GrAppliedClip&&,
637 const GrDstProxyView&,
638 GrXferBarrierFlags renderPassXferBarriers,
639 GrLoadOp colorLoadOp) override;
640
641 void onPrepareDraws(GrMeshDrawTarget*) override;
642 void onExecute(GrOpFlushState*, const SkRect& chainBounds) override;
643 #if defined(GR_TEST_UTILS)
644 SkString onDumpInfo() const override;
645 #endif
646
647 GrGeometryProcessor* makeGP(SkArenaAlloc*);
648
649 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps&) override;
650
651 /**
652 * Built either from a SkMesh or a SkVertices. In the former case the data is owned
653 * by Mesh and in the latter it is not. Meshes made from SkVertices can contain a SkMatrix
654 * to enable CPU-based transformation but Meshes made from SkMesh cannot.
655 */
656 class Mesh {
657 public:
658 Mesh() = delete;
659 explicit Mesh(const SkMesh& mesh);
660 Mesh(sk_sp<SkVertices>, const SkMatrix& viewMatrix);
661 Mesh(const Mesh&) = delete;
662 Mesh(Mesh&& m);
663
664 Mesh& operator=(const Mesh&) = delete;
665 Mesh& operator=(Mesh&&) = delete; // not used by SkSTArray but could be implemented.
666
667 ~Mesh();
668
isFromVertices() const669 bool isFromVertices() const { return SkToBool(fVertices); }
670
vertices() const671 const SkVertices* vertices() const {
672 SkASSERT(this->isFromVertices());
673 return fVertices.get();
674 }
675
gpuVB() const676 std::tuple<sk_sp<const GrGpuBuffer>, size_t> gpuVB() const {
677 if (this->isFromVertices()) {
678 return {};
679 }
680 SkASSERT(fMeshData.vb);
681 if (!fMeshData.vb->isGaneshBacked()) {
682 // This is a signal to upload the vertices which weren't already uploaded
683 // to the GPU (e.g. SkPicture containing a mesh).
684 return {nullptr, 0};
685 }
686 if (auto buf = static_cast<const SkMeshPriv::GaneshVertexBuffer*>(fMeshData.vb.get())) {
687 return {buf->asGpuBuffer(), fMeshData.voffset};
688 }
689 return {};
690 }
691
gpuIB() const692 std::tuple<sk_sp<const GrGpuBuffer>, size_t> gpuIB() const {
693 if (this->isFromVertices() || !fMeshData.ib) {
694 return {};
695 }
696 if (!fMeshData.ib->isGaneshBacked()) {
697 // This is a signal to upload the indices which weren't already uploaded
698 // to the GPU (e.g. SkPicture containing a mesh).
699 return {nullptr, 0};
700 }
701 if (auto buf = static_cast<const SkMeshPriv::GaneshIndexBuffer*>(fMeshData.ib.get())) {
702 return {buf->asGpuBuffer(), fMeshData.ioffset};
703 }
704 return {};
705 }
706
707 void writeVertices(skgpu::VertexWriter& writer,
708 const SkMeshSpecification& spec,
709 bool transform) const;
710
vertexCount() const711 int vertexCount() const {
712 return this->isFromVertices() ? fVertices->priv().vertexCount() : fMeshData.vcount;
713 }
714
indices() const715 const uint16_t* indices() const {
716 if (this->isFromVertices()) {
717 return fVertices->priv().indices();
718 }
719 if (!fMeshData.ib) {
720 return nullptr;
721 }
722 auto data = fMeshData.ib->peek();
723 if (!data) {
724 return nullptr;
725 }
726 return SkTAddOffset<const uint16_t>(data, fMeshData.ioffset);
727 }
728
indexCount() const729 int indexCount() const {
730 return this->isFromVertices() ? fVertices->priv().indexCount() : fMeshData.icount;
731 }
732
733 using sk_is_trivially_relocatable = std::true_type;
734
735 private:
736 struct MeshData {
737 sk_sp<const SkMeshPriv::VB> vb;
738 sk_sp<const SkMeshPriv::IB> ib;
739
740 size_t vcount = 0;
741 size_t icount = 0;
742
743 size_t voffset = 0;
744 size_t ioffset = 0;
745
746 static_assert(::sk_is_trivially_relocatable<decltype(vb)>::value);
747 static_assert(::sk_is_trivially_relocatable<decltype(ib)>::value);
748
749 using sk_is_trivially_relocatable = std::true_type;
750 };
751
752 sk_sp<SkVertices> fVertices;
753
754 union {
755 SkMatrix fViewMatrix;
756 MeshData fMeshData;
757 };
758
759 static_assert(::sk_is_trivially_relocatable<decltype(fVertices)>::value);
760 static_assert(::sk_is_trivially_relocatable<decltype(fViewMatrix)>::value);
761 };
762
763 Helper fHelper;
764 sk_sp<SkMeshSpecification> fSpecification;
765 bool fIgnoreSpecColor = false;
766 GrPrimitiveType fPrimitiveType;
767 STArray<1, Mesh> fMeshes;
768 sk_sp<GrColorSpaceXform> fColorSpaceXform;
769 SkPMColor4f fColor; // Used if no color from spec or analysis overrides.
770 SkMatrix fViewMatrix;
771 sk_sp<const SkData> fUniforms;
772 int fVertexCount;
773 int fIndexCount;
774 GrSimpleMesh* fMesh = nullptr;
775 GrProgramInfo* fProgramInfo = nullptr;
776 TArray<std::unique_ptr<GrFragmentProcessor>> fChildren;
777
778 using INHERITED = GrMeshDrawOp;
779 };
780
Mesh(const SkMesh & mesh)781 MeshOp::Mesh::Mesh(const SkMesh& mesh) {
782 new (&fMeshData) MeshData();
783 SkASSERT(mesh.vertexBuffer());
784 fMeshData.vb = sk_ref_sp(static_cast<SkMeshPriv::VB*>(mesh.vertexBuffer()));
785 if (mesh.indexBuffer()) {
786 fMeshData.ib = sk_ref_sp(static_cast<SkMeshPriv::IB*>(mesh.indexBuffer()));
787 }
788 fMeshData.vcount = mesh.vertexCount();
789 fMeshData.voffset = mesh.vertexOffset();
790 fMeshData.icount = mesh.indexCount();
791 fMeshData.ioffset = mesh.indexOffset();
792
793 // The caller could modify CPU buffers after the draw so we must copy the data.
794 if (fMeshData.vb->peek()) {
795 auto data = SkTAddOffset<const void>(fMeshData.vb->peek(), fMeshData.voffset);
796 size_t size = fMeshData.vcount * mesh.spec()->stride();
797 fMeshData.vb = SkMeshPriv::CpuVertexBuffer::Make(data, size);
798 fMeshData.voffset = 0;
799 }
800
801 if (fMeshData.ib && fMeshData.ib->peek()) {
802 auto data = SkTAddOffset<const void>(fMeshData.ib->peek(), fMeshData.ioffset);
803 size_t size = fMeshData.icount * sizeof(uint16_t);
804 fMeshData.ib = SkMeshPriv::CpuIndexBuffer::Make(data, size);
805 fMeshData.ioffset = 0;
806 }
807 }
808
Mesh(sk_sp<SkVertices> vertices,const SkMatrix & viewMatrix)809 MeshOp::Mesh::Mesh(sk_sp<SkVertices> vertices, const SkMatrix& viewMatrix)
810 : fVertices(std::move(vertices)), fViewMatrix(viewMatrix) {
811 SkASSERT(fVertices);
812 }
813
Mesh(Mesh && that)814 MeshOp::Mesh::Mesh(Mesh&& that) {
815 fVertices = std::move(that.fVertices);
816 if (fVertices) {
817 fViewMatrix = that.fViewMatrix;
818 // 'that' is now not-a-vertices. Make sure it can be safely destroyed.
819 new (&that.fMeshData) MeshData();
820 } else {
821 fMeshData = std::move(that.fMeshData);
822 }
823 }
824
~Mesh()825 MeshOp::Mesh::~Mesh() {
826 if (!this->isFromVertices()) {
827 fMeshData.~MeshData();
828 }
829 }
830
writeVertices(skgpu::VertexWriter & writer,const SkMeshSpecification & spec,bool transform) const831 void MeshOp::Mesh::writeVertices(skgpu::VertexWriter& writer,
832 const SkMeshSpecification& spec,
833 bool transform) const {
834 SkASSERT(!transform || this->isFromVertices());
835 if (this->isFromVertices()) {
836 int vertexCount = fVertices->priv().vertexCount();
837 for (int i = 0; i < vertexCount; ++i) {
838 SkPoint pos = fVertices->priv().positions()[i];
839 if (transform) {
840 SkASSERT(!fViewMatrix.hasPerspective());
841 fViewMatrix.mapPoints(&pos, 1);
842 }
843 writer << pos;
844 if (SkMeshSpecificationPriv::HasColors(spec)) {
845 SkASSERT(fVertices->priv().hasColors());
846 writer << fVertices->priv().colors()[i];
847 }
848 if (fVertices->priv().hasTexCoords()) {
849 writer << fVertices->priv().texCoords()[i];
850 }
851 }
852 } else {
853 const void* data = fMeshData.vb->peek();
854 if (data) {
855 auto vdata = SkTAddOffset<const char>(data, fMeshData.voffset);
856 writer << skgpu::VertexWriter::Array(vdata, spec.stride()*fMeshData.vcount);
857 }
858 }
859 }
860
MeshOp(GrProcessorSet * processorSet,const SkPMColor4f & color,const SkMesh & mesh,TArray<std::unique_ptr<GrFragmentProcessor>> children,GrAAType aaType,sk_sp<GrColorSpaceXform> colorSpaceXform,const SkMatrix & viewMatrix)861 MeshOp::MeshOp(GrProcessorSet* processorSet,
862 const SkPMColor4f& color,
863 const SkMesh& mesh,
864 TArray<std::unique_ptr<GrFragmentProcessor>> children,
865 GrAAType aaType,
866 sk_sp<GrColorSpaceXform> colorSpaceXform,
867 const SkMatrix& viewMatrix)
868 : INHERITED(ClassID())
869 , fHelper(processorSet, aaType)
870 , fPrimitiveType(primitive_type(mesh.mode()))
871 , fColorSpaceXform(std::move(colorSpaceXform))
872 , fColor(color)
873 , fViewMatrix(viewMatrix) {
874 fMeshes.emplace_back(mesh);
875
876 fSpecification = mesh.refSpec();
877 fUniforms = SkRuntimeEffectPriv::TransformUniforms(
878 mesh.spec()->uniforms(), mesh.refUniforms(), mesh.spec()->colorSpace());
879
880 fChildren = std::move(children);
881
882 fVertexCount = fMeshes.back().vertexCount();
883 fIndexCount = fMeshes.back().indexCount();
884
885 this->setTransformedBounds(mesh.bounds(), fViewMatrix, HasAABloat::kNo, IsHairline::kNo);
886 }
887
make_vertices_spec(bool hasColors,bool hasTex)888 static SkMeshSpecification* make_vertices_spec(bool hasColors, bool hasTex) {
889 using Attribute = SkMeshSpecification::Attribute;
890 using Varying = SkMeshSpecification::Varying;
891 std::vector<Attribute> attributes;
892 attributes.reserve(3);
893 attributes.push_back({Attribute::Type::kFloat2, 0, SkString{"pos"}});
894 size_t size = 8;
895
896 std::vector<Varying> varyings;
897 attributes.reserve(2);
898
899 SkString vs("Varyings main(const Attributes a) {\nVaryings v;");
900 SkString fs("float2 ");
901
902 if (hasColors) {
903 attributes.push_back({Attribute::Type::kUByte4_unorm, size, SkString{"color"}});
904 varyings.push_back({Varying::Type::kHalf4, SkString{"color"}});
905 vs += "v.color = a.color;\n";
906 // Using float4 for the output color to work around skbug.com/12761
907 fs += "main(const Varyings v, out float4 color) {\n"
908 "color = float4(v.color.bgr*v.color.a, v.color.a);\n";
909 size += 4;
910 } else {
911 fs += "main(const Varyings v) {\n";
912 }
913
914 if (hasTex) {
915 attributes.push_back({Attribute::Type::kFloat2, size, SkString{"tex"}});
916 varyings.push_back({Varying::Type::kFloat2, SkString{"tex"}});
917 vs += "v.tex = a.tex;\n";
918 fs += "return v.tex;\n";
919 size += 8;
920 } else {
921 fs += "return v.position;\n";
922 }
923 vs += "v.position = a.pos;\nreturn v;\n}";
924 fs += "}";
925 auto [spec, error] = SkMeshSpecification::Make(SkSpan(attributes),
926 size,
927 SkSpan(varyings),
928 vs,
929 fs);
930 SkASSERT(spec);
931 return spec.release();
932 }
933
MeshOp(GrProcessorSet * processorSet,const SkPMColor4f & color,sk_sp<SkVertices> vertices,const GrPrimitiveType * overridePrimitiveType,GrAAType aaType,sk_sp<GrColorSpaceXform> colorSpaceXform,const SkMatrix & viewMatrix)934 MeshOp::MeshOp(GrProcessorSet* processorSet,
935 const SkPMColor4f& color,
936 sk_sp<SkVertices> vertices,
937 const GrPrimitiveType* overridePrimitiveType,
938 GrAAType aaType,
939 sk_sp<GrColorSpaceXform> colorSpaceXform,
940 const SkMatrix& viewMatrix)
941 : INHERITED(ClassID())
942 , fHelper(processorSet, aaType)
943 , fColorSpaceXform(std::move(colorSpaceXform))
944 , fColor(color)
945 , fViewMatrix(viewMatrix) {
946 int attrs = (vertices->priv().hasColors() ? 0b01 : 0b00) |
947 (vertices->priv().hasTexCoords() ? 0b10 : 0b00);
948 switch (attrs) {
949 case 0b00: {
950 static const SkMeshSpecification* kSpec = make_vertices_spec(false, false);
951 fSpecification = sk_ref_sp(kSpec);
952 break;
953 }
954 case 0b01: {
955 static const SkMeshSpecification* kSpec = make_vertices_spec(true, false);
956 fSpecification = sk_ref_sp(kSpec);
957 break;
958 }
959 case 0b10: {
960 static const SkMeshSpecification* kSpec = make_vertices_spec(false, true);
961 fSpecification = sk_ref_sp(kSpec);
962 break;
963 }
964 case 0b11: {
965 static const SkMeshSpecification* kSpec = make_vertices_spec(true, true);
966 fSpecification = sk_ref_sp(kSpec);
967 break;
968 }
969 }
970 SkASSERT(fSpecification);
971
972 if (overridePrimitiveType) {
973 fPrimitiveType = *overridePrimitiveType;
974 } else {
975 switch (vertices->priv().mode()) {
976 case SkVertices::kTriangles_VertexMode:
977 fPrimitiveType = GrPrimitiveType::kTriangles;
978 break;
979 case SkVertices::kTriangleStrip_VertexMode:
980 fPrimitiveType = GrPrimitiveType::kTriangleStrip;
981 break;
982 case SkVertices::kTriangleFan_VertexMode:
983 SkUNREACHABLE;
984 }
985 }
986
987 IsHairline isHairline = IsHairline::kNo;
988 if (GrIsPrimTypeLines(fPrimitiveType) || fPrimitiveType == GrPrimitiveType::kPoints) {
989 isHairline = IsHairline::kYes;
990 }
991 this->setTransformedBounds(vertices->bounds(), fViewMatrix, HasAABloat::kNo, isHairline);
992
993 fMeshes.emplace_back(std::move(vertices), fViewMatrix);
994
995 fVertexCount = fMeshes.back().vertexCount();
996 fIndexCount = fMeshes.back().indexCount();
997 }
998
999 #if defined(GR_TEST_UTILS)
onDumpInfo() const1000 SkString MeshOp::onDumpInfo() const { return {}; }
1001 #endif
1002
fixedFunctionFlags() const1003 GrDrawOp::FixedFunctionFlags MeshOp::fixedFunctionFlags() const {
1004 return fHelper.fixedFunctionFlags();
1005 }
1006
finalize(const GrCaps & caps,const GrAppliedClip * clip,GrClampType clampType)1007 GrProcessorSet::Analysis MeshOp::finalize(const GrCaps& caps,
1008 const GrAppliedClip* clip,
1009 GrClampType clampType) {
1010 GrProcessorAnalysisColor gpColor;
1011 gpColor.setToUnknown();
1012 auto result = fHelper.finalizeProcessors(caps,
1013 clip,
1014 clampType,
1015 GrProcessorAnalysisCoverage::kNone,
1016 &gpColor);
1017 if (gpColor.isConstant(&fColor)) {
1018 fIgnoreSpecColor = true;
1019 }
1020 return result;
1021 }
1022
makeGP(SkArenaAlloc * arena)1023 GrGeometryProcessor* MeshOp::makeGP(SkArenaAlloc* arena) {
1024 std::optional<SkPMColor4f> color;
1025 if (fIgnoreSpecColor || !SkMeshSpecificationPriv::HasColors(*fSpecification)) {
1026 color.emplace(fColor);
1027 }
1028 // Check if we're pre-transforming the vertices on the CPU.
1029 const SkMatrix& vm = fViewMatrix == SkMatrix::InvalidMatrix() ? SkMatrix::I() : fViewMatrix;
1030 return MeshGP::Make(arena,
1031 fSpecification,
1032 fColorSpaceXform,
1033 vm,
1034 color,
1035 fHelper.usesLocalCoords(),
1036 fUniforms,
1037 SkSpan(fChildren));
1038 }
1039
onCreateProgramInfo(const GrCaps * caps,SkArenaAlloc * arena,const GrSurfaceProxyView & writeView,bool usesMSAASurface,GrAppliedClip && appliedClip,const GrDstProxyView & dstProxyView,GrXferBarrierFlags renderPassXferBarriers,GrLoadOp colorLoadOp)1040 void MeshOp::onCreateProgramInfo(const GrCaps* caps,
1041 SkArenaAlloc* arena,
1042 const GrSurfaceProxyView& writeView,
1043 bool usesMSAASurface,
1044 GrAppliedClip&& appliedClip,
1045 const GrDstProxyView& dstProxyView,
1046 GrXferBarrierFlags renderPassXferBarriers,
1047 GrLoadOp colorLoadOp) {
1048 fProgramInfo = fHelper.createProgramInfo(caps,
1049 arena,
1050 writeView,
1051 usesMSAASurface,
1052 std::move(appliedClip),
1053 dstProxyView,
1054 this->makeGP(arena),
1055 fPrimitiveType,
1056 renderPassXferBarriers,
1057 colorLoadOp);
1058 }
1059
onPrepareDraws(GrMeshDrawTarget * target)1060 void MeshOp::onPrepareDraws(GrMeshDrawTarget* target) {
1061 size_t vertexStride = fSpecification->stride();
1062 sk_sp<const GrBuffer> vertexBuffer;
1063 int firstVertex;
1064 std::tie(vertexBuffer, firstVertex) = fMeshes[0].gpuVB();
1065
1066 if (!vertexBuffer) {
1067 skgpu::VertexWriter verts = target->makeVertexWriter(vertexStride,
1068 fVertexCount,
1069 &vertexBuffer,
1070 &firstVertex);
1071 if (!verts) {
1072 SkDebugf("Could not allocate vertices.\n");
1073 return;
1074 }
1075
1076 bool transform = fViewMatrix == SkMatrix::InvalidMatrix();
1077 for (const auto& m : fMeshes) {
1078 m.writeVertices(verts, *fSpecification, transform);
1079 }
1080 } else {
1081 SkASSERT(fMeshes.size() == 1);
1082 SkASSERT(firstVertex % fSpecification->stride() == 0);
1083 firstVertex /= fSpecification->stride();
1084 }
1085
1086 sk_sp<const GrBuffer> indexBuffer;
1087 int firstIndex = 0;
1088
1089 std::tie(indexBuffer, firstIndex) = fMeshes[0].gpuIB();
1090 if (fIndexCount && !indexBuffer) {
1091 uint16_t* indices = nullptr;
1092 indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1093 if (!indices) {
1094 SkDebugf("Could not allocate indices.\n");
1095 return;
1096 }
1097 // We can just copy the first mesh's indices. Subsequent meshes need their indices adjusted.
1098 std::copy_n(fMeshes[0].indices(), fMeshes[0].indexCount(), indices);
1099 int voffset = fMeshes[0].vertexCount();
1100 int ioffset = fMeshes[0].indexCount();
1101 for (int m = 1; m < fMeshes.size(); ++m) {
1102 for (int i = 0; i < fMeshes[m].indexCount(); ++i) {
1103 indices[ioffset++] = fMeshes[m].indices()[i] + voffset;
1104 }
1105 voffset += fMeshes[m].vertexCount();
1106 }
1107 SkASSERT(voffset == fVertexCount);
1108 SkASSERT(ioffset == fIndexCount);
1109 } else if (indexBuffer) {
1110 SkASSERT(fMeshes.size() == 1);
1111 SkASSERT(firstIndex % sizeof(uint16_t) == 0);
1112 firstIndex /= sizeof(uint16_t);
1113 }
1114
1115 SkASSERT(!fMesh);
1116 fMesh = target->allocMesh();
1117
1118 if (indexBuffer) {
1119 fMesh->setIndexed(std::move(indexBuffer),
1120 fIndexCount,
1121 firstIndex,
1122 /*minIndexValue=*/0,
1123 fVertexCount - 1,
1124 GrPrimitiveRestart::kNo,
1125 std::move(vertexBuffer),
1126 firstVertex);
1127 } else {
1128 fMesh->set(std::move(vertexBuffer), fVertexCount, firstVertex);
1129 }
1130 }
1131
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)1132 void MeshOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
1133 if (!fProgramInfo) {
1134 this->createProgramInfo(flushState);
1135 }
1136
1137 if (!fProgramInfo || !fMesh) {
1138 return;
1139 }
1140
1141 TArray<GrSurfaceProxy*> geomProcTextures;
1142 for (const std::unique_ptr<GrFragmentProcessor>& fp : fChildren) {
1143 if (fp) {
1144 fp->visitTextureEffects([&](const GrTextureEffect& te) {
1145 geomProcTextures.push_back(te.view().proxy());
1146 });
1147 }
1148 }
1149
1150 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
1151 flushState->bindTextures(fProgramInfo->geomProc(),
1152 geomProcTextures.data(),
1153 fProgramInfo->pipeline());
1154 flushState->drawMesh(*fMesh);
1155 }
1156
onCombineIfPossible(GrOp * t,SkArenaAlloc *,const GrCaps & caps)1157 GrOp::CombineResult MeshOp::onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) {
1158 auto that = t->cast<MeshOp>();
1159 if (!fMeshes[0].isFromVertices() || !that->fMeshes[0].isFromVertices()) {
1160 // We *could* make this work when the vertex/index buffers are CPU-backed but that isn't an
1161 // important use case.
1162 return GrOp::CombineResult::kCannotCombine;
1163 }
1164
1165 // Check for a combinable primitive type.
1166 if (!(fPrimitiveType == GrPrimitiveType::kTriangles ||
1167 fPrimitiveType == GrPrimitiveType::kLines ||
1168 fPrimitiveType == GrPrimitiveType::kPoints)) {
1169 return CombineResult::kCannotCombine;
1170 }
1171
1172 if (fPrimitiveType != that->fPrimitiveType) {
1173 return CombineResult::kCannotCombine;
1174 }
1175
1176 if (fVertexCount > INT32_MAX - that->fVertexCount) {
1177 return CombineResult::kCannotCombine;
1178 }
1179 if (SkToBool(fIndexCount) != SkToBool(that->fIndexCount)) {
1180 return CombineResult::kCannotCombine;
1181 }
1182 if (SkToBool(fIndexCount) && fVertexCount > SkToInt(UINT16_MAX) - that->fVertexCount) {
1183 return CombineResult::kCannotCombine;
1184 }
1185
1186 if (SkMeshSpecificationPriv::Hash(*this->fSpecification) !=
1187 SkMeshSpecificationPriv::Hash(*that->fSpecification)) {
1188 return CombineResult::kCannotCombine;
1189 }
1190
1191 // Our specs made for vertices don't have uniforms.
1192 SkASSERT(fSpecification->uniforms().empty());
1193
1194 if (!SkMeshSpecificationPriv::HasColors(*fSpecification) && fColor != that->fColor) {
1195 return CombineResult::kCannotCombine;
1196 }
1197
1198 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
1199 return CombineResult::kCannotCombine;
1200 }
1201
1202 if (fViewMatrix != that->fViewMatrix) {
1203 // If we use local coords and the local coords come from positions then we can't pre-
1204 // transform the positions on the CPU.
1205 if (fHelper.usesLocalCoords() && !fMeshes[0].vertices()->priv().hasTexCoords()) {
1206 return CombineResult::kCannotCombine;
1207 }
1208 // We only support two-component position attributes. This means we would not get
1209 // perspective-correct interpolation of attributes if we transform on the CPU.
1210 if ((this->fViewMatrix.isFinite() && this->fViewMatrix.hasPerspective()) ||
1211 (that->fViewMatrix.isFinite() && that->fViewMatrix.hasPerspective())) {
1212 return CombineResult::kCannotCombine;
1213 }
1214 // This is how we record that we must CPU-transform the vertices.
1215 fViewMatrix = SkMatrix::InvalidMatrix();
1216 }
1217
1218 // NOTE: The source color space is part of the spec, and the destination gamut is determined by
1219 // the render target context. A mis-match should be impossible.
1220 SkASSERT(GrColorSpaceXform::Equals(fColorSpaceXform.get(), that->fColorSpaceXform.get()));
1221
1222 fMeshes.move_back_n(that->fMeshes.size(), that->fMeshes.begin());
1223 fVertexCount += that->fVertexCount;
1224 fIndexCount += that->fIndexCount;
1225 return CombineResult::kMerged;
1226 }
1227
1228 } // anonymous namespace
1229
1230 namespace skgpu::ganesh::DrawMeshOp {
1231
Make(GrRecordingContext * context,GrPaint && paint,const SkMesh & mesh,TArray<std::unique_ptr<GrFragmentProcessor>> children,const SkMatrix & viewMatrix,GrAAType aaType,sk_sp<GrColorSpaceXform> colorSpaceXform)1232 GrOp::Owner Make(GrRecordingContext* context,
1233 GrPaint&& paint,
1234 const SkMesh& mesh,
1235 TArray<std::unique_ptr<GrFragmentProcessor>> children,
1236 const SkMatrix& viewMatrix,
1237 GrAAType aaType,
1238 sk_sp<GrColorSpaceXform> colorSpaceXform) {
1239 return GrSimpleMeshDrawOpHelper::FactoryHelper<MeshOp>(context,
1240 std::move(paint),
1241 mesh,
1242 std::move(children),
1243 aaType,
1244 std::move(colorSpaceXform),
1245 viewMatrix);
1246 }
1247
Make(GrRecordingContext * context,GrPaint && paint,sk_sp<SkVertices> vertices,const GrPrimitiveType * overridePrimitiveType,const SkMatrix & viewMatrix,GrAAType aaType,sk_sp<GrColorSpaceXform> colorSpaceXform)1248 GrOp::Owner Make(GrRecordingContext* context,
1249 GrPaint&& paint,
1250 sk_sp<SkVertices> vertices,
1251 const GrPrimitiveType* overridePrimitiveType,
1252 const SkMatrix& viewMatrix,
1253 GrAAType aaType,
1254 sk_sp<GrColorSpaceXform> colorSpaceXform) {
1255 return GrSimpleMeshDrawOpHelper::FactoryHelper<MeshOp>(context,
1256 std::move(paint),
1257 std::move(vertices),
1258 overridePrimitiveType,
1259 aaType,
1260 std::move(colorSpaceXform),
1261 viewMatrix);
1262 }
1263
1264 } // namespace skgpu::ganesh::DrawMeshOp
1265