blob: 96a3d0eef49db3432ca4e1ff4d59b3a77fb0b3a3 [file] [edit]
/*
* Copyright 2026 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/graphite/render/MeshRenderStep.h"
#include "src/gpu/graphite/PaintParamsKey.h"
#include "include/private/SkAssert.h"
#include "include/private/SkDebug.h"
#include "src/core/SkMeshPriv.h"
#include "src/core/SkRuntimeEffectPriv.h"
#include "src/core/SkSLTypeShared.h"
#include "src/core/SkVertState.h"
#include "src/gpu/BufferWriter.h"
#include "src/gpu/graphite/DrawParams.h"
#include "src/gpu/graphite/DrawTypes.h"
#include "src/gpu/graphite/DrawWriter.h"
#include "src/gpu/graphite/PipelineData.h"
#include "src/gpu/graphite/ShaderCodeDictionary.h"
#include "src/gpu/graphite/geom/Geometry.h"
#include "src/gpu/graphite/geom/Transform.h"
#include "src/sksl/SkSLString.h"
#include "src/gpu/graphite/render/CommonDepthStencilSettings.h"
#include <cstdint>
namespace skgpu::graphite {
static constexpr char kMeshFSLocalCoordsName[] = "meshLocalCoordsOverride";
static constexpr std::initializer_list<Uniform> kStepUniforms =
{{"depth", SkSLType::kFloat}, {"localToDevice", SkSLType::kFloat4x4}};
static SkVertices::VertexMode vertex_mode(SkMesh::Mode mode) {
if (mode == SkMesh::Mode::kTriangles) {
return SkVertices::VertexMode::kTriangles_VertexMode;
} else {
SkASSERT(mode == SkMesh::Mode::kTriangleStrip);
return SkVertices::VertexMode::kTriangleStrip_VertexMode;
}
}
MeshRenderStep::MeshRenderStep(Layout layout)
: RenderStep(layout,
RenderStep::RenderStepID::kMesh,
Flags::kPerformsShading | Flags::kAppendVertices
| Flags::kEmitsPrimitiveColor,
kStepUniforms,
PrimitiveType::kTriangles,
kDirectDepthLEqualPass,
/*staticAttrs=*/{},
/*appendAttrs=*/{{"ssboIndex", VertexAttribType::kUInt, SkSLType::kUInt}},
/*storageUniforms=*/{}) {}
MeshRenderStep::~MeshRenderStep() {}
std::string MeshRenderStep::vertexSkSL(const RootNodesInfo& roots) const {
const SkMeshSpecification* spec = roots.fMeshSpec;
SkASSERT(spec);
// Any attributes and varyings defined by the SkMeshSpecification will be emitted within
// `ShaderInfo::generateVertexSkSL` with the same names as defined in the mesh specification,
// we mangle the varyings' names to ensure there is not a name conflict between an attribute
// and varying.
std::string attrs = "Attributes attributes;\n";
for (const auto& attr : spec->attributes()) {
attrs += SkSL::String::printf("attributes.%s = %s;\n",
attr.name.c_str(), attr.name.c_str());
}
std::string varyingAssignments;
for (const auto& v : SkMeshSpecificationPriv::Varyings(*spec)) {
varyingAssignments += SkSL::String::printf("%s%s = varyings.%s;\n",
v.name.c_str(), kMeshVaryingMangleSuffix,
v.name.c_str());
}
return SkSL::String::printf("%s\n"
"Varyings varyings = %s(attributes);\n"
"float4 devPosition"
" = localToDevice * float4(varyings.position, depth, 1.0);\n"
"stepLocalCoords = varyings.position;\n"
"%s", attrs.c_str(), kMeshVSMainName,
varyingAssignments.c_str());
}
std::string MeshRenderStep::fragmentColorSkSL(const RootNodesInfo& roots) const {
const SkMeshSpecification* spec = roots.fMeshSpec;
SkASSERT(spec);
// Varyings defined by the SkMeshSpecification will be emitted within
// `ShaderInfo::generateFragmentSkSL` following the same varying name mangling.
std::string s = "Varyings varyings;\n";
for (const auto& v : SkMeshSpecificationPriv::Varyings(*spec)) {
s += SkSL::String::printf("varyings.%s = %s%s;\n",
v.name.c_str(), v.name.c_str(),
kMeshVaryingMangleSuffix);
}
bool needsColorConversion = SkMeshSpecificationPriv::GetColorType(*spec)
== SkMeshSpecificationPriv::ColorType::kFloat4;
std::string outColorName = "primitiveColor";
if (needsColorConversion) {
outColorName = "primitiveColorFloat4";
s += "float4 " + outColorName + ";\n";
}
// Check if the mesh FS should have a primitive color output parameter.
bool hasColorOutput = SkMeshSpecificationPriv::HasColors(*spec);
std::string methodCall = hasColorOutput ? SkSL::String::printf("%s(varyings, %s)",
kMeshFSMainName,
outColorName.c_str())
: SkSL::String::printf("%s(varyings)",
kMeshFSMainName);
s += SkSL::String::printf("float2 %s = %s;\n",
kMeshFSLocalCoordsName,
methodCall.c_str());
if (needsColorConversion) {
s += "primitiveColor = half4(" + outColorName + ");\n";
}
return s;
}
const char* MeshRenderStep::fragmentColorSkSLLocalCoordsVariable() const {
return kMeshFSLocalCoordsName;
}
void MeshRenderStep::writeVertices(DrawWriter* writer,
StorageContext* /*storageContext*/,
const DrawParams& params,
uint32_t ssboIndex) const {
const SkMesh& mesh = params.geometry().mesh();
const SkMeshSpecification* spec = mesh.spec();
SkASSERT(mesh.vertexBuffer()); // SkMesh::isValid() should catch this in Device::drawMesh().
auto* cpuVertexBuffer = static_cast<const SkMeshPriv::CpuVertexBuffer*>(mesh.vertexBuffer());
size_t vertexStride = spec->stride();
size_t vertexCount = mesh.vertexCount();
const uint8_t* vertexData = static_cast<const uint8_t*>(
cpuVertexBuffer->peek()) + mesh.vertexOffset();
const uint16_t* indices = nullptr;
size_t indexCount = mesh.indexCount();
if (mesh.indexBuffer()) {
auto* cpuIndexBuffer = static_cast<const SkMeshPriv::CpuIndexBuffer*>(mesh.indexBuffer());
indices = reinterpret_cast<const uint16_t*>(static_cast<const uint8_t*>(
cpuIndexBuffer->peek()) + mesh.indexOffset());
}
DrawWriter::Vertices verts(*writer);
verts.reserve(indices ? indexCount : vertexCount);
VertState state(vertexCount, indices, indexCount);
VertState::Proc vertProc = state.chooseProc(vertex_mode(mesh.mode()));
while (vertProc(&state)) {
VertexWriter vertWriter = verts.append(3);
for (uint32_t i = 0; i < 3; ++i) {
uint32_t vertIndex = i == 0 ? state.f0 : (i == 1 ? state.f1 : state.f2);
const uint8_t* vertexDataBase = vertexData + vertIndex * vertexStride;
vertWriter << ssboIndex;
for (const SkMeshSpecification::Attribute& attr : spec->attributes()) {
vertWriter << VertexWriter::Array(
vertexDataBase + attr.offset,
SkMeshSpecificationPriv::AttrTypeByteSize(attr.type));
}
}
}
}
void MeshRenderStep::writeUniformsAndTextures(const DrawParams& params,
PipelineDataGatherer* gatherer) const {
const SkMesh& mesh = params.geometry().mesh();
const SkMeshSpecification* spec = mesh.spec();
#if defined(SK_DEBUG)
skia_private::TArray<Uniform> uniforms(kStepUniforms);
uniforms.reserve_exact(this->numUniforms() + spec->uniforms().size());
skia_private::TArray<std::string> uniformNames;
uniformNames.reserve_exact(spec->uniforms().size());
for (const auto& u : spec->uniforms()) {
uniformNames.push_back(std::string(u.name));
uniforms.push_back(Uniform(uniformNames.back().c_str(),
ShaderCodeDictionary::UniformTypeToSkSLType(u),
u.isArray() ? u.count : Uniform::kNonArray));
}
gatherer->checkRewind();
UniformExpectationsValidator uev(gatherer, uniforms);
#endif
// Write step uniforms.
gatherer->write(params.order().depthAsFloat());
gatherer->write(params.transform().matrix());
// Write SkMeshSpecification uniforms.
sk_sp<const SkData> transformedUniforms = SkRuntimeEffectPriv::TransformUniforms(
spec->uniforms(), mesh.refUniforms(), SkMeshSpecificationPriv::ColorSpace(*spec));
for (const SkMeshSpecification::Uniform& u: spec->uniforms()) {
// Since we are doing pointer arithmetic, make sure we are operating on bytes.
static_assert(std::is_same_v<decltype(transformedUniforms->bytes()), const uint8_t*>);
// We only need the type information for writing the uniform so we can leave the name null.
gatherer->write(Uniform(nullptr,
ShaderCodeDictionary::UniformTypeToSkSLType(u),
u.isArray() ? u.count : Uniform::kNonArray),
transformedUniforms->bytes() + u.offset);
}
}
size_t MeshRenderStep::appendDataStride(const DrawParams& params) const {
const SkMeshSpecification* spec = params.geometry().mesh().spec();
size_t stride = this->RenderStep::appendDataStride(params);
for (const auto& attr : spec->attributes()) {
stride += Attribute::MakeFromSkMeshAttribute(attr).sizeAlign4();
}
return stride;
}
} // namespace skgpu::graphite