| /* |
| * Copyright 2021 Google LLC |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "src/gpu/ops/DrawCustomMeshOp.h" |
| |
| #include "include/core/SkCustomMesh.h" |
| #include "src/core/SkArenaAlloc.h" |
| #include "src/core/SkCustomMeshPriv.h" |
| #include "src/core/SkVerticesPriv.h" |
| #include "src/gpu/BufferWriter.h" |
| #include "src/gpu/GrGeometryProcessor.h" |
| #include "src/gpu/GrOpFlushState.h" |
| #include "src/gpu/GrProgramInfo.h" |
| #include "src/gpu/KeyBuilder.h" |
| #include "src/gpu/glsl/GrGLSLColorSpaceXformHelper.h" |
| #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h" |
| #include "src/gpu/glsl/GrGLSLVarying.h" |
| #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h" |
| #include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h" |
| #include "src/sksl/codegen/SkSLPipelineStageCodeGenerator.h" |
| #include "src/sksl/ir/SkSLProgram.h" |
| |
| namespace { |
| |
| GrPrimitiveType primitive_type(SkCustomMesh::Mode mode) { |
| switch (mode) { |
| case SkCustomMesh::Mode::kTriangles: return GrPrimitiveType::kTriangles; |
| case SkCustomMesh::Mode::kTriangleStrip: return GrPrimitiveType::kTriangleStrip; |
| } |
| SkUNREACHABLE; |
| } |
| |
| class CustomMeshGP : public GrGeometryProcessor { |
| public: |
| static GrGeometryProcessor* Make(SkArenaAlloc* arena, |
| sk_sp<SkCustomMeshSpecification> spec, |
| sk_sp<GrColorSpaceXform> colorSpaceXform, |
| const SkMatrix& viewMatrix, |
| const skstd::optional<SkPMColor4f>& color, |
| bool needsLocalCoords) { |
| return arena->make([&](void* ptr) { |
| return new (ptr) CustomMeshGP(std::move(spec), |
| std::move(colorSpaceXform), |
| viewMatrix, |
| std::move(color), |
| needsLocalCoords); |
| }); |
| } |
| |
| const char* name() const override { return "CustomMeshGP"; } |
| |
| void addToKey(const GrShaderCaps& caps, skgpu::KeyBuilder* b) const override { |
| b->add32(SkCustomMeshSpecificationPriv::Hash(*fSpec), "custom mesh spec hash"); |
| b->add32(ProgramImpl::ComputeMatrixKey(caps, fViewMatrix), "view matrix key"); |
| if (SkCustomMeshSpecificationPriv::GetColorType(*fSpec) != |
| SkCustomMeshSpecificationPriv::ColorType::kNone) { |
| b->add32(GrColorSpaceXform::XformKey(fColorSpaceXform.get()), "colorspace xform key"); |
| } |
| } |
| |
| std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override { |
| return std::make_unique<Impl>(); |
| } |
| |
| private: |
| class Impl : public ProgramImpl { |
| public: |
| void setData(const GrGLSLProgramDataManager& pdman, |
| const GrShaderCaps& shaderCaps, |
| const GrGeometryProcessor& geomProc) override { |
| const auto& cmgp = geomProc.cast<CustomMeshGP>(); |
| SetTransform(pdman, shaderCaps, fViewMatrixUniform, cmgp.fViewMatrix, &fViewMatrix); |
| fColorSpaceHelper.setData(pdman, cmgp.fColorSpaceXform.get()); |
| if (fColorUniform.isValid()) { |
| pdman.set4fv(fColorUniform, 1, cmgp.fColor.vec()); |
| } |
| } |
| |
| private: |
| class MeshCallbacks : public SkSL::PipelineStage::Callbacks { |
| public: |
| MeshCallbacks(Impl* self, |
| GrGLSLShaderBuilder* builder, |
| const char* mainName, |
| const SkSL::Context& context) |
| : fSelf(self), fBuilder(builder), fMainName(mainName), fContext(context) {} |
| |
| using String = SkSL::String; |
| |
| String declareUniform(const SkSL::VarDeclaration* decl) override { |
| SK_ABORT("uniforms not allowed"); |
| } |
| |
| String getMangledName(const char* name) override { |
| return String(fBuilder->getMangledFunctionName(name).c_str()); |
| } |
| |
| String getMainName() override { return fMainName; } |
| |
| void defineFunction(const char* decl, const char* body, bool isMain) override { |
| fBuilder->emitFunction(decl, body); |
| } |
| |
| void declareFunction(const char* decl) override { |
| fBuilder->emitFunctionPrototype(decl); |
| } |
| |
| void defineStruct(const char* definition) override { |
| fBuilder->definitionAppend(definition); |
| } |
| |
| void declareGlobal(const char* declaration) override { |
| fBuilder->definitionAppend(declaration); |
| } |
| |
| String sampleShader(int index, String coords) override { |
| SK_ABORT("No children allowed."); |
| } |
| |
| String sampleColorFilter(int index, String color) override { |
| SK_ABORT("No children allowed."); |
| } |
| |
| String sampleBlender(int index, String src, String dst) override { |
| SK_ABORT("No children allowed."); |
| } |
| |
| String toLinearSrgb(String color) override { |
| SK_ABORT("Color transform intrinsics not allowed."); |
| } |
| |
| String fromLinearSrgb(String Color) override { |
| SK_ABORT("Color transform intrinsics not allowed."); |
| } |
| |
| Impl* fSelf; |
| GrGLSLShaderBuilder* fBuilder; |
| const char* fMainName; |
| const SkSL::Context& fContext; |
| }; |
| |
| void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { |
| const CustomMeshGP& cmgp = args.fGeomProc.cast<CustomMeshGP>(); |
| GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; |
| GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; |
| GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; |
| GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; |
| |
| ////// VS |
| |
| // emit attributes |
| varyingHandler->emitAttributes(cmgp); |
| |
| // Define the user's vert function. |
| SkString userVertName = vertBuilder->getMangledFunctionName("custom_mesh_vs"); |
| const SkSL::Program* customVS = SkCustomMeshSpecificationPriv::VS(*cmgp.fSpec); |
| MeshCallbacks vsCallbacks(this, vertBuilder, userVertName.c_str(), *customVS->fContext); |
| SkSL::PipelineStage::ConvertProgram(*customVS, |
| /*sampleCoords=*/"", |
| /*inputColor=*/"", |
| /*destColor=*/"", |
| &vsCallbacks); |
| |
| // Copy the individual attributes into a struct |
| vertBuilder->codeAppendf("%s attributes;", |
| vsCallbacks.getMangledName("Attributes").c_str()); |
| size_t i = 0; |
| SkASSERT(cmgp.vertexAttributes().count() == (int)cmgp.fSpec->attributes().size()); |
| for (auto attr : cmgp.vertexAttributes()) { |
| vertBuilder->codeAppendf("attributes.%s = %s;", |
| cmgp.fSpec->attributes()[i++].name.c_str(), |
| attr.name()); |
| } |
| |
| // Call the user's vert function. |
| vertBuilder->codeAppendf("%s varyings;", |
| vsCallbacks.getMangledName("Varyings").c_str()); |
| vertBuilder->codeAppendf("float2 pos = %s(attributes, varyings);", |
| userVertName.c_str()); |
| |
| // Unpack the varyings from the struct into individual varyings. |
| std::vector<GrGLSLVarying> varyings; |
| varyings.reserve(SkCustomMeshSpecificationPriv::Varyings(*cmgp.fSpec).size()); |
| for (const auto& v : SkCustomMeshSpecificationPriv::Varyings(*cmgp.fSpec)) { |
| varyings.emplace_back(SkCustomMeshSpecificationPriv::VaryingTypeAsSLType(v.type)); |
| varyingHandler->addVarying(v.name.c_str(), &varyings.back()); |
| vertBuilder->codeAppendf("%s = varyings.%s;", |
| varyings.back().vsOut(), |
| v.name.c_str()); |
| } |
| |
| // Setup position |
| WriteOutputPosition(vertBuilder, |
| uniformHandler, |
| *args.fShaderCaps, |
| gpArgs, |
| "pos", |
| cmgp.fViewMatrix, |
| &fViewMatrixUniform); |
| |
| ////// FS |
| |
| fragBuilder->codeAppendf("half4 %s;", args.fOutputColor); |
| fragBuilder->codeAppendf("const half4 %s = half4(1);", args.fOutputCoverage); |
| |
| // Define the user's frag function. |
| SkString userFragName = fragBuilder->getMangledFunctionName("custom_mesh_fs"); |
| const SkSL::Program* customFS = SkCustomMeshSpecificationPriv::FS(*cmgp.fSpec); |
| MeshCallbacks fsCallbacks(this, fragBuilder, userFragName.c_str(), *customFS->fContext); |
| SkSL::PipelineStage::ConvertProgram(*customFS, |
| /*sampleCoords=*/"", |
| /*inputColor=*/"", |
| /*destColor=*/"", |
| &fsCallbacks); |
| |
| // Pack the varyings into a struct to call the user's frag code. |
| fragBuilder->codeAppendf("%s varyings;", |
| fsCallbacks.getMangledName("Varyings").c_str()); |
| i = 0; |
| for (const auto& varying : SkCustomMeshSpecificationPriv::Varyings(*cmgp.fSpec)) { |
| fragBuilder->codeAppendf("varyings.%s = %s;", |
| varying.name.c_str(), |
| varyings[i++].vsOut()); |
| } |
| SkCustomMeshSpecificationPriv::ColorType meshColorType = |
| SkCustomMeshSpecificationPriv::GetColorType(*cmgp.fSpec); |
| const char* uniformColorName = nullptr; |
| if (cmgp.fColor != SK_PMColor4fILLEGAL) { |
| fColorUniform = uniformHandler->addUniform(nullptr, |
| kFragment_GrShaderFlag, |
| kHalf4_GrSLType, |
| "color", |
| &uniformColorName); |
| } |
| SkString localCoordAssignment; |
| if (SkCustomMeshSpecificationPriv::HasLocalCoords(*cmgp.fSpec) && |
| cmgp.fNeedsLocalCoords) { |
| localCoordAssignment = "float2 local ="; |
| } |
| if (meshColorType == SkCustomMeshSpecificationPriv::ColorType::kNone) { |
| fragBuilder->codeAppendf("%s %s(varyings);", |
| localCoordAssignment.c_str(), |
| userFragName.c_str()); |
| SkASSERT(uniformColorName); |
| fragBuilder->codeAppendf("%s = %s;", args.fOutputColor, uniformColorName); |
| } else { |
| fColorSpaceHelper.emitCode(uniformHandler, |
| cmgp.fColorSpaceXform.get(), |
| kFragment_GrShaderFlag); |
| if (meshColorType == SkCustomMeshSpecificationPriv::ColorType::kFloat4) { |
| fragBuilder->codeAppendf("float4 color;"); |
| } else { |
| SkASSERT(meshColorType == SkCustomMeshSpecificationPriv::ColorType::kHalf4); |
| fragBuilder->codeAppendf("half4 color;"); |
| } |
| |
| fragBuilder->codeAppendf("%s %s(varyings, color);", |
| localCoordAssignment.c_str(), |
| userFragName.c_str()); |
| // We ignore the user's color if analysis told us to emit a specific color. The user |
| // color might be float4 and we expect a half4 in the colorspace helper. |
| const char* color = uniformColorName ? uniformColorName : "half4(color)"; |
| SkString xformedColor; |
| fragBuilder->appendColorGamutXform(&xformedColor, color, &fColorSpaceHelper); |
| fragBuilder->codeAppendf("%s = %s;", args.fOutputColor, xformedColor.c_str()); |
| } |
| if (cmgp.fNeedsLocalCoords) { |
| if (SkCustomMeshSpecificationPriv::HasLocalCoords(*cmgp.fSpec)) { |
| gpArgs->fLocalCoordVar = GrShaderVar("local", kFloat2_GrSLType); |
| gpArgs->fLocalCoordShader = kFragment_GrShaderType; |
| } else { |
| gpArgs->fLocalCoordVar = GrShaderVar("pos", kFloat2_GrSLType); |
| gpArgs->fLocalCoordShader = kVertex_GrShaderType; |
| } |
| } |
| } |
| |
| private: |
| SkMatrix fViewMatrix = SkMatrix::InvalidMatrix(); |
| |
| UniformHandle fViewMatrixUniform; |
| UniformHandle fColorUniform; |
| |
| GrGLSLColorSpaceXformHelper fColorSpaceHelper; |
| }; |
| |
| CustomMeshGP(sk_sp<SkCustomMeshSpecification> spec, |
| sk_sp<GrColorSpaceXform> colorSpaceXform, |
| const SkMatrix& viewMatrix, |
| const skstd::optional<SkPMColor4f>& color, |
| bool needsLocalCoords) |
| : INHERITED(kVerticesGP_ClassID) |
| , fSpec(std::move(spec)) |
| , fViewMatrix(viewMatrix) |
| , fColorSpaceXform(std::move(colorSpaceXform)) |
| , fNeedsLocalCoords(needsLocalCoords) { |
| fColor = color.value_or(SK_PMColor4fILLEGAL); |
| for (const auto& srcAttr : fSpec->attributes()) { |
| fAttributes.emplace_back( |
| srcAttr.name.c_str(), |
| SkCustomMeshSpecificationPriv::AttrTypeAsVertexAttribType(srcAttr.type), |
| SkCustomMeshSpecificationPriv::AttrTypeAsSLType(srcAttr.type), |
| srcAttr.offset); |
| } |
| this->setVertexAttributes(fAttributes.data(), fAttributes.size(), fSpec->stride()); |
| } |
| |
| sk_sp<SkCustomMeshSpecification> fSpec; |
| std::vector<Attribute> fAttributes; |
| SkMatrix fViewMatrix; |
| SkPMColor4f fColor; |
| sk_sp<GrColorSpaceXform> fColorSpaceXform; |
| bool fNeedsLocalCoords; |
| |
| using INHERITED = GrGeometryProcessor; |
| }; |
| |
| class CustomMeshOp final : public GrMeshDrawOp { |
| private: |
| using Helper = GrSimpleMeshDrawOpHelper; |
| |
| public: |
| DEFINE_OP_CLASS_ID |
| |
| CustomMeshOp(GrProcessorSet*, |
| const SkPMColor4f&, |
| SkCustomMesh, |
| GrAAType, |
| sk_sp<GrColorSpaceXform>, |
| const SkMatrixProvider&); |
| |
| CustomMeshOp(GrProcessorSet*, |
| const SkPMColor4f&, |
| sk_sp<SkVertices>, |
| const GrPrimitiveType*, |
| GrAAType, |
| sk_sp<GrColorSpaceXform>, |
| const SkMatrixProvider&); |
| |
| const char* name() const override { return "CustomMeshOp"; } |
| |
| void visitProxies(const GrVisitProxyFunc& func) const override { |
| if (fProgramInfo) { |
| fProgramInfo->visitFPProxies(func); |
| } else { |
| fHelper.visitProxies(func); |
| } |
| } |
| |
| FixedFunctionFlags fixedFunctionFlags() const override; |
| |
| GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, GrClampType) override; |
| |
| private: |
| GrProgramInfo* programInfo() override { return fProgramInfo; } |
| |
| void onCreateProgramInfo(const GrCaps*, |
| SkArenaAlloc*, |
| const GrSurfaceProxyView& writeView, |
| bool usesMSAASurface, |
| GrAppliedClip&&, |
| const GrDstProxyView&, |
| GrXferBarrierFlags renderPassXferBarriers, |
| GrLoadOp colorLoadOp) override; |
| |
| void onPrepareDraws(GrMeshDrawTarget*) override; |
| void onExecute(GrOpFlushState*, const SkRect& chainBounds) override; |
| #if GR_TEST_UTILS |
| SkString onDumpInfo() const override; |
| #endif |
| |
| GrGeometryProcessor* makeGP(SkArenaAlloc*); |
| |
| CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps&) override; |
| |
| /** |
| * Built either from a SkCustomMesh or a SkVertices. In the former case the data is owned |
| * by Mesh and in the latter it is not. Meshes made from SkVertices can contain a SkMatrix |
| * to enable CPU-based transformation but Meshes made from SkCustomMesh cannot. |
| */ |
| class Mesh { |
| public: |
| Mesh() = delete; |
| Mesh(const SkCustomMesh& cm); |
| Mesh(sk_sp<SkVertices>, const SkMatrix& viewMatrix); |
| Mesh(const Mesh&) = delete; |
| Mesh(Mesh&& m); |
| |
| Mesh& operator=(const Mesh&) = delete; |
| Mesh& operator=(Mesh&&) = delete; // not used by SkSTArray but could be implemented. |
| |
| ~Mesh(); |
| |
| bool isFromVertices() const { return SkToBool(fVertices); } |
| |
| void writeVertices(skgpu::VertexWriter& writer, |
| const SkCustomMeshSpecification& spec, |
| bool transform) const; |
| |
| int vertexCount() const { |
| return this->isFromVertices() ? fVertices->priv().vertexCount() : fCMData.vcount; |
| } |
| |
| const uint16_t* indices() const { |
| return this->isFromVertices() ? fVertices->priv().indices() : fCMData.indices; |
| } |
| |
| int indexCount() const { |
| return this->isFromVertices() ? fVertices->priv().indexCount() : fCMData.icount; |
| } |
| |
| private: |
| struct CMData { |
| const char* vb; |
| const uint16_t* indices; |
| int vcount; |
| int icount; |
| }; |
| |
| sk_sp<SkVertices> fVertices; |
| |
| union { |
| SkMatrix fViewMatrix; |
| CMData fCMData; |
| }; |
| }; |
| |
| Helper fHelper; |
| sk_sp<SkCustomMeshSpecification> fSpecification; |
| bool fIgnoreSpecColor = false; |
| GrPrimitiveType fPrimitiveType; |
| SkSTArray<1, Mesh> fMeshes; |
| sk_sp<GrColorSpaceXform> fColorSpaceXform; |
| SkPMColor4f fColor; // Used if no color from spec or analysis overrides. |
| SkMatrix fViewMatrix; |
| int fVertexCount; |
| int fIndexCount; |
| GrSimpleMesh* fMesh = nullptr; |
| GrProgramInfo* fProgramInfo = nullptr; |
| |
| using INHERITED = GrMeshDrawOp; |
| }; |
| |
| CustomMeshOp::Mesh::Mesh(const SkCustomMesh& cm) { |
| fCMData.vb = SkCopyCustomMeshVB(cm).release(); |
| fCMData.indices = SkCopyCustomMeshIB(cm).release(); |
| fCMData.vcount = cm.vcount; |
| fCMData.icount = cm.icount; |
| } |
| |
| CustomMeshOp::Mesh::Mesh(sk_sp<SkVertices> vertices, const SkMatrix& viewMatrix) |
| : fVertices(std::move(vertices)), fViewMatrix(viewMatrix) { |
| SkASSERT(fVertices); |
| } |
| |
| CustomMeshOp::Mesh::Mesh(Mesh&& m) { |
| fVertices = std::move(m.fVertices); |
| if (fVertices) { |
| fViewMatrix = m.fViewMatrix; |
| } else { |
| fCMData = m.fCMData; |
| } |
| m.fCMData.vb = nullptr; |
| m.fCMData.indices = nullptr; |
| } |
| |
| CustomMeshOp::Mesh::~Mesh() { |
| if (!this->isFromVertices()) { |
| delete[] fCMData.indices; |
| delete[] fCMData.vb; |
| } |
| } |
| |
| void CustomMeshOp::Mesh::writeVertices(skgpu::VertexWriter& writer, |
| const SkCustomMeshSpecification& spec, |
| bool transform) const { |
| SkASSERT(!transform || this->isFromVertices()); |
| if (this->isFromVertices()) { |
| int vertexCount = fVertices->priv().vertexCount(); |
| for (int i = 0; i < vertexCount; ++i) { |
| SkPoint pos = fVertices->priv().positions()[i]; |
| if (transform) { |
| SkASSERT(!fViewMatrix.hasPerspective()); |
| fViewMatrix.mapPoints(&pos, 1); |
| } |
| writer << pos; |
| if (SkCustomMeshSpecificationPriv::HasColors(spec)) { |
| SkASSERT(fVertices->priv().hasColors()); |
| writer << fVertices->priv().colors()[i]; |
| } |
| if (SkCustomMeshSpecificationPriv::HasLocalCoords(spec)) { |
| SkASSERT(fVertices->priv().hasTexCoords()); |
| writer << fVertices->priv().texCoords()[i]; |
| } |
| } |
| } else { |
| writer << skgpu::VertexWriter::Array(fCMData.vb, spec.stride()*fCMData.vcount); |
| } |
| } |
| |
| CustomMeshOp::CustomMeshOp(GrProcessorSet* processorSet, |
| const SkPMColor4f& color, |
| SkCustomMesh cm, |
| GrAAType aaType, |
| sk_sp<GrColorSpaceXform> colorSpaceXform, |
| const SkMatrixProvider& matrixProvider) |
| : INHERITED(ClassID()) |
| , fHelper(processorSet, aaType) |
| , fPrimitiveType(primitive_type(cm.mode)) |
| , fColorSpaceXform(std::move(colorSpaceXform)) |
| , fColor(color) |
| , fViewMatrix(matrixProvider.localToDevice()) { |
| fMeshes.emplace_back(cm); |
| |
| fSpecification = std::move(cm.spec); |
| |
| fVertexCount = fMeshes.back().vertexCount(); |
| fIndexCount = fMeshes.back().indexCount(); |
| |
| this->setTransformedBounds(cm.bounds, fViewMatrix, HasAABloat::kNo, IsHairline::kNo); |
| } |
| |
| static sk_sp<SkCustomMeshSpecification> make_vertices_spec(bool hasColors, bool hasTex) { |
| using Attribute = SkCustomMeshSpecification::Attribute; |
| using Varying = SkCustomMeshSpecification::Varying; |
| std::vector<Attribute> attributes; |
| attributes.reserve(3); |
| attributes.push_back({Attribute::Type::kFloat2, 0, SkString{"pos"}}); |
| size_t size = 8; |
| |
| std::vector<Varying> varyings; |
| attributes.reserve(2); |
| |
| SkString vs("float2 main(Attributes a, out Varyings v) {\n"); |
| SkString fs(hasTex ? "float2 " : "void "); |
| |
| if (hasColors) { |
| attributes.push_back({Attribute::Type::kUByte4_unorm, size, SkString{"color"}}); |
| varyings.push_back({Varying::Type::kHalf4, SkString{"color"}}); |
| vs += "v.color = a.color;\n"; |
| // Using float4 for the output color to work around skbug.com/12761 |
| fs += "main(Varyings v, out float4 color) {\n" |
| "color = float4(v.color.bgr*v.color.a, v.color.a);\n"; |
| size += 4; |
| } else { |
| fs += "main(Varyings v) {\n"; |
| } |
| |
| if (hasTex) { |
| attributes.push_back({Attribute::Type::kFloat2, size, SkString{"tex"}}); |
| varyings.push_back({Varying::Type::kFloat2, SkString{"tex"}}); |
| vs += "v.tex = a.tex;\n"; |
| fs += "return v.tex;\n"; |
| size += 8; |
| } |
| vs += "return a.pos;\n}"; |
| fs += "}"; |
| auto [spec, error] = SkCustomMeshSpecification::Make( |
| SkMakeSpan(attributes), |
| size, |
| SkMakeSpan(varyings), |
| vs, |
| fs); |
| SkASSERT(spec); |
| return spec; |
| } |
| |
| CustomMeshOp::CustomMeshOp(GrProcessorSet* processorSet, |
| const SkPMColor4f& color, |
| sk_sp<SkVertices> vertices, |
| const GrPrimitiveType* overridePrimitiveType, |
| GrAAType aaType, |
| sk_sp<GrColorSpaceXform> colorSpaceXform, |
| const SkMatrixProvider& matrixProvider) |
| : INHERITED(ClassID()) |
| , fHelper(processorSet, aaType) |
| , fColorSpaceXform(std::move(colorSpaceXform)) |
| , fColor(color) |
| , fViewMatrix(matrixProvider.localToDevice()) { |
| int attrs = (vertices->priv().hasColors() ? 0b01 : 0b00) | |
| (vertices->priv().hasTexCoords() ? 0b10 : 0b00); |
| switch (attrs) { |
| case 0b00: { |
| static const auto kSpec = make_vertices_spec(false, false); |
| fSpecification = kSpec; |
| break; |
| } |
| case 0b01: { |
| static const auto kSpec = make_vertices_spec(true, false); |
| fSpecification = kSpec; |
| break; |
| } |
| case 0b10: { |
| static const auto kSpec = make_vertices_spec(false, true); |
| fSpecification = kSpec; |
| break; |
| } |
| case 0b11: { |
| static const auto kSpec = make_vertices_spec(true, true); |
| fSpecification = kSpec; |
| break; |
| } |
| } |
| SkASSERT(fSpecification); |
| |
| if (overridePrimitiveType) { |
| fPrimitiveType = *overridePrimitiveType; |
| } else { |
| switch (vertices->priv().mode()) { |
| case SkVertices::kTriangles_VertexMode: |
| fPrimitiveType = GrPrimitiveType::kTriangles; |
| break; |
| case SkVertices::kTriangleStrip_VertexMode: |
| fPrimitiveType = GrPrimitiveType::kTriangleStrip; |
| break; |
| case SkVertices::kTriangleFan_VertexMode: |
| SkUNREACHABLE; |
| } |
| } |
| |
| IsHairline isHairline = IsHairline::kNo; |
| if (GrIsPrimTypeLines(fPrimitiveType) || fPrimitiveType == GrPrimitiveType::kPoints) { |
| isHairline = IsHairline::kYes; |
| } |
| this->setTransformedBounds(vertices->bounds(), fViewMatrix, HasAABloat::kNo, isHairline); |
| |
| fMeshes.emplace_back(std::move(vertices), fViewMatrix); |
| |
| fVertexCount = fMeshes.back().vertexCount(); |
| fIndexCount = fMeshes.back().indexCount(); |
| } |
| |
| #if GR_TEST_UTILS |
| SkString CustomMeshOp::onDumpInfo() const { return {}; } |
| #endif |
| |
| GrDrawOp::FixedFunctionFlags CustomMeshOp::fixedFunctionFlags() const { |
| return fHelper.fixedFunctionFlags(); |
| } |
| |
| GrProcessorSet::Analysis CustomMeshOp::finalize(const GrCaps& caps, |
| const GrAppliedClip* clip, |
| GrClampType clampType) { |
| GrProcessorAnalysisColor gpColor; |
| gpColor.setToUnknown(); |
| auto result = fHelper.finalizeProcessors(caps, |
| clip, |
| clampType, |
| GrProcessorAnalysisCoverage::kNone, |
| &gpColor); |
| if (gpColor.isConstant(&fColor)) { |
| fIgnoreSpecColor = true; |
| } |
| return result; |
| } |
| |
| GrGeometryProcessor* CustomMeshOp::makeGP(SkArenaAlloc* arena) { |
| skstd::optional<SkPMColor4f> color; |
| if (fIgnoreSpecColor || !SkCustomMeshSpecificationPriv::HasColors(*fSpecification)) { |
| color.emplace(fColor); |
| } |
| // Check if we're pre-transforming the vertices on the CPU. |
| const SkMatrix& vm = fViewMatrix == SkMatrix::InvalidMatrix() ? SkMatrix::I() : fViewMatrix; |
| return CustomMeshGP::Make(arena, |
| fSpecification, |
| fColorSpaceXform, |
| vm, |
| color, |
| fHelper.usesLocalCoords()); |
| } |
| |
| void CustomMeshOp::onCreateProgramInfo(const GrCaps* caps, |
| SkArenaAlloc* arena, |
| const GrSurfaceProxyView& writeView, |
| bool usesMSAASurface, |
| GrAppliedClip&& appliedClip, |
| const GrDstProxyView& dstProxyView, |
| GrXferBarrierFlags renderPassXferBarriers, |
| GrLoadOp colorLoadOp) { |
| fProgramInfo = fHelper.createProgramInfo(caps, |
| arena, |
| writeView, |
| usesMSAASurface, |
| std::move(appliedClip), |
| dstProxyView, |
| this->makeGP(arena), |
| fPrimitiveType, |
| renderPassXferBarriers, |
| colorLoadOp); |
| } |
| |
| void CustomMeshOp::onPrepareDraws(GrMeshDrawTarget* target) { |
| size_t vertexStride = fSpecification->stride(); |
| sk_sp<const GrBuffer> vertexBuffer; |
| int firstVertex = 0; |
| skgpu::VertexWriter verts{target->makeVertexSpace(vertexStride, |
| fVertexCount, |
| &vertexBuffer, |
| &firstVertex)}; |
| if (!verts) { |
| SkDebugf("Could not allocate vertices.\n"); |
| return; |
| } |
| |
| bool transform = fViewMatrix == SkMatrix::InvalidMatrix(); |
| for (const auto& m : fMeshes) { |
| m.writeVertices(verts, *fSpecification, transform); |
| } |
| |
| sk_sp<const GrBuffer> indexBuffer; |
| int firstIndex = 0; |
| uint16_t* indices = nullptr; |
| if (fIndexCount) { |
| indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex); |
| if (!indices) { |
| SkDebugf("Could not allocate indices.\n"); |
| return; |
| } |
| // We can just copy the first mesh's indices. Subsequent meshes need their indices adjusted. |
| std::copy_n(fMeshes[0].indices(), fMeshes[0].indexCount(), indices); |
| int voffset = fMeshes[0].vertexCount(); |
| int ioffset = fMeshes[0].indexCount(); |
| for (size_t m = 1; m < fMeshes.size(); ++m) { |
| for (int i = 0; i < fMeshes[m].indexCount(); ++i) { |
| indices[ioffset++] = fMeshes[m].indices()[i] + voffset; |
| } |
| voffset += fMeshes[m].vertexCount(); |
| } |
| SkASSERT(voffset == fVertexCount); |
| SkASSERT(ioffset == fIndexCount); |
| } |
| |
| SkASSERT(!fMesh); |
| fMesh = target->allocMesh(); |
| |
| if (indices) { |
| fMesh->setIndexed(std::move(indexBuffer), |
| fIndexCount, |
| firstIndex, |
| /*minIndexValue=*/0, |
| fVertexCount, |
| GrPrimitiveRestart::kNo, |
| std::move(vertexBuffer), |
| firstVertex); |
| } else { |
| fMesh->set(std::move(vertexBuffer), fVertexCount, firstVertex); |
| } |
| } |
| |
| void CustomMeshOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) { |
| if (!fProgramInfo) { |
| this->createProgramInfo(flushState); |
| } |
| |
| if (!fProgramInfo || !fMesh) { |
| return; |
| } |
| |
| flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds); |
| flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline()); |
| flushState->drawMesh(*fMesh); |
| } |
| |
| GrOp::CombineResult CustomMeshOp::onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) { |
| auto that = t->cast<CustomMeshOp>(); |
| |
| // Check for a combinable primitive type. |
| if (!(fPrimitiveType == GrPrimitiveType::kTriangles || |
| fPrimitiveType == GrPrimitiveType::kLines || |
| fPrimitiveType == GrPrimitiveType::kPoints)) { |
| return CombineResult::kCannotCombine; |
| } |
| |
| if (fPrimitiveType != that->fPrimitiveType) { |
| return CombineResult::kCannotCombine; |
| } |
| |
| if (SkToBool(fIndexCount) != SkToBool(that->fIndexCount)) { |
| return CombineResult::kCannotCombine; |
| } |
| if (SkToBool(fIndexCount) && fVertexCount + that->fVertexCount > SkToInt(UINT16_MAX)) { |
| return CombineResult::kCannotCombine; |
| } |
| |
| if (SkCustomMeshSpecificationPriv::Hash(*this->fSpecification) != |
| SkCustomMeshSpecificationPriv::Hash(*that->fSpecification)) { |
| return CombineResult::kCannotCombine; |
| } |
| |
| if (!SkCustomMeshSpecificationPriv::HasColors(*fSpecification) && fColor != that->fColor) { |
| return CombineResult::kCannotCombine; |
| } |
| |
| if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) { |
| return CombineResult::kCannotCombine; |
| } |
| |
| if (fViewMatrix != that->fViewMatrix) { |
| if (!fMeshes[0].isFromVertices() || !that->fMeshes[0].isFromVertices()) { |
| // We don't know how to CPU transform actual custom meshes on CPU. |
| return CombineResult::kCannotCombine; |
| } |
| // If we use local coords and the local coords come from positions then we can't pre- |
| // transform the positions on the CPU. |
| if (fHelper.usesLocalCoords() && |
| !SkCustomMeshSpecificationPriv::HasLocalCoords(*fSpecification)) { |
| return CombineResult::kCannotCombine; |
| } |
| // We only support two-component position attributes. This means we would not get |
| // perspective-correct interpolation of attributes if we transform on the CPU. |
| if ((this->fViewMatrix.isFinite() && this->fViewMatrix.hasPerspective()) || |
| (that->fViewMatrix.isFinite() && that->fViewMatrix.hasPerspective())) { |
| return CombineResult::kCannotCombine; |
| } |
| // This is how we record that we must CPU-transform the vertices. |
| fViewMatrix = SkMatrix::InvalidMatrix(); |
| } |
| |
| // NOTE: The source color space is part of the spec, and the destination gamut is determined by |
| // the render target context. A mis-match should be impossible. |
| SkASSERT(GrColorSpaceXform::Equals(fColorSpaceXform.get(), that->fColorSpaceXform.get())); |
| |
| fMeshes.move_back_n(that->fMeshes.count(), that->fMeshes.begin()); |
| fVertexCount += that->fVertexCount; |
| fIndexCount += that->fIndexCount; |
| return CombineResult::kMerged; |
| } |
| |
| } // anonymous namespace |
| |
| namespace skgpu::v1::DrawCustomMeshOp { |
| |
| GrOp::Owner Make(GrRecordingContext* context, |
| GrPaint&& paint, |
| SkCustomMesh cm, |
| const SkMatrixProvider& matrixProvider, |
| GrAAType aaType, |
| sk_sp<GrColorSpaceXform> colorSpaceXform) { |
| return GrSimpleMeshDrawOpHelper::FactoryHelper<CustomMeshOp>(context, |
| std::move(paint), |
| std::move(cm), |
| aaType, |
| std::move(colorSpaceXform), |
| matrixProvider); |
| } |
| |
| GrOp::Owner Make(GrRecordingContext* context, |
| GrPaint&& paint, |
| sk_sp<SkVertices> vertices, |
| const GrPrimitiveType* overridePrimitiveType, |
| const SkMatrixProvider& matrixProvider, |
| GrAAType aaType, |
| sk_sp<GrColorSpaceXform> colorSpaceXform) { |
| return GrSimpleMeshDrawOpHelper::FactoryHelper<CustomMeshOp>(context, |
| std::move(paint), |
| std::move(vertices), |
| overridePrimitiveType, |
| aaType, |
| std::move(colorSpaceXform), |
| matrixProvider); |
| } |
| |
| } // namespace skgpu::v1::DrawCustomMeshOp |