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