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