blob: 80e64c0a3d3a987ec575777650ee37f7da2b02c8 [file] [log] [blame]
/*
* 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 std::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) {}
std::string declareUniform(const SkSL::VarDeclaration* decl) override {
SK_ABORT("uniforms not allowed");
}
std::string getMangledName(const char* name) override {
return std::string(fBuilder->getMangledFunctionName(name).c_str());
}
std::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);
}
std::string sampleShader(int index, std::string coords) override {
SK_ABORT("No children allowed.");
}
std::string sampleColorFilter(int index, std::string color) override {
SK_ABORT("No children allowed.");
}
std::string sampleBlender(int index, std::string src, std::string dst) override {
SK_ABORT("No children allowed.");
}
std::string toLinearSrgb(std::string color) override {
SK_ABORT("Color transform intrinsics not allowed.");
}
std::string fromLinearSrgb(std::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,
SkSLType::kHalf4,
"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", SkSLType::kFloat2);
gpArgs->fLocalCoordShader = kFragment_GrShaderType;
} else {
gpArgs->fLocalCoordVar = GrShaderVar("pos", SkSLType::kFloat2);
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 std::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) {
std::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->makeVertexWriter(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