blob: 7bf08be372d5adb2b7b19f613d528f1d56b35bd0 [file] [log] [blame]
/*
* Copyright 2022 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/TessellateWedgesRenderStep.h"
#include "include/private/SkSLString.h"
#include "src/gpu/graphite/BufferManager.h"
#include "src/gpu/graphite/DrawParams.h"
#include "src/gpu/graphite/DrawWriter.h"
#include "src/gpu/graphite/PipelineData.h"
#include "src/gpu/graphite/render/DynamicInstancesPatchAllocator.h"
#include "src/gpu/tessellate/FixedCountBufferUtils.h"
#include "src/gpu/tessellate/MidpointContourParser.h"
#include "src/gpu/tessellate/PatchWriter.h"
namespace skgpu::graphite {
namespace {
using namespace skgpu::tess;
// Only kFanPoint, no stroke params, since this is for filled wedges.
// No color or wide color attribs, since it might always be part of the PaintParams
// or we'll add a color-only fast path to RenderStep later.
// No explicit curve type on platforms that support infinity.
static constexpr PatchAttribs kAttribs = PatchAttribs::kFanPoint |
PatchAttribs::kPaintDepth |
PatchAttribs::kSsboIndex;
static constexpr PatchAttribs kAttribsWithCurveType = kAttribs | PatchAttribs::kExplicitCurveType;
using Writer = PatchWriter<DynamicInstancesPatchAllocator<FixedCountWedges>,
Required<PatchAttribs::kFanPoint>,
Required<PatchAttribs::kPaintDepth>,
Required<PatchAttribs::kSsboIndex>,
Optional<PatchAttribs::kExplicitCurveType>>;
// The order of the attribute declarations must match the order used by
// PatchWriter::emitPatchAttribs, i.e.:
// join << fanPoint << stroke << color << depth << curveType << ssboIndex
static constexpr Attribute kBaseAttributes[] = {
{"p01", VertexAttribType::kFloat4, SkSLType::kFloat4},
{"p23", VertexAttribType::kFloat4, SkSLType::kFloat4},
{"fanPointAttrib", VertexAttribType::kFloat2, SkSLType::kFloat2},
{"depth", VertexAttribType::kFloat, SkSLType::kFloat},
{"ssboIndex", VertexAttribType::kInt, SkSLType::kInt}};
static constexpr Attribute kAttributesWithCurveType[] = {
{"p01", VertexAttribType::kFloat4, SkSLType::kFloat4},
{"p23", VertexAttribType::kFloat4, SkSLType::kFloat4},
{"fanPointAttrib", VertexAttribType::kFloat2, SkSLType::kFloat2},
{"depth", VertexAttribType::kFloat, SkSLType::kFloat},
{"curveType", VertexAttribType::kFloat, SkSLType::kFloat},
{"ssboIndex", VertexAttribType::kInt, SkSLType::kInt}};
static constexpr SkSpan<const Attribute> kAttributes[2] = {kAttributesWithCurveType,
kBaseAttributes};
} // namespace
TessellateWedgesRenderStep::TessellateWedgesRenderStep(std::string_view variantName,
bool infinitySupport,
DepthStencilSettings depthStencilSettings,
StaticBufferManager* bufferManager)
: RenderStep("TessellateWedgesRenderStep",
variantName,
Flags::kRequiresMSAA |
(depthStencilSettings.fDepthWriteEnabled ? Flags::kPerformsShading
: Flags::kNone),
/*uniforms=*/{{"localToDevice", SkSLType::kFloat4x4}},
PrimitiveType::kTriangles,
depthStencilSettings,
/*vertexAttrs=*/ {{"resolveLevel_and_idx",
VertexAttribType::kFloat2, SkSLType::kFloat2}},
/*instanceAttrs=*/kAttributes[infinitySupport])
, fInfinitySupport(infinitySupport) {
SkASSERT(this->instanceStride() ==
PatchStride(infinitySupport ? kAttribs : kAttribsWithCurveType));
// Initialize the static buffers we'll use when recording draw calls.
// NOTE: Each instance of this RenderStep gets its own copy of the data. If this ends up causing
// problems, we can modify StaticBufferManager to de-duplicate requests.
const size_t vertexSize = FixedCountWedges::VertexBufferSize();
auto vertexData = bufferManager->getVertexWriter(vertexSize, &fVertexBuffer);
FixedCountWedges::WriteVertexBuffer(std::move(vertexData), vertexSize);
const size_t indexSize = FixedCountWedges::IndexBufferSize();
auto indexData = bufferManager->getIndexWriter(indexSize, &fIndexBuffer);
FixedCountWedges::WriteIndexBuffer(std::move(indexData), indexSize);
}
TessellateWedgesRenderStep::~TessellateWedgesRenderStep() {}
std::string TessellateWedgesRenderStep::vertexSkSL() const {
return SkSL::String::printf(
R"(
float2 localCoord;
if (resolveLevel_and_idx.x < 0) {
// A negative resolve level means this is the fan point.
localCoord = fanPointAttrib;
} else {
// TODO: Approximate perspective scaling to match how PatchWriter is configured
// (or provide explicit tessellation level in instance data instead of
// replicating work)
float2x2 vectorXform = float2x2(localToDevice[0].xy, localToDevice[1].xy);
localCoord = tessellate_filled_curve(
vectorXform, resolveLevel_and_idx.x, resolveLevel_and_idx.y, p01, p23, %s);
}
float4 devPosition = localToDevice * float4(localCoord, 0.0, 1.0);
devPosition.z = depth;
stepLocalCoords = localCoord;
)",
fInfinitySupport ? "curve_type_using_inf_support(p23)" : "curveType");
}
void TessellateWedgesRenderStep::writeVertices(DrawWriter* dw,
const DrawParams& params,
int ssboIndex) const {
SkPath path = params.geometry().shape().asPath(); // TODO: Iterate the Shape directly
int patchReserveCount = FixedCountWedges::PreallocCount(path.countVerbs());
Writer writer{fInfinitySupport ? kAttribs : kAttribsWithCurveType,
*dw,
fVertexBuffer,
fIndexBuffer,
patchReserveCount};
writer.updatePaintDepthAttrib(params.order().depthAsFloat());
writer.updateSsboIndexAttrib(ssboIndex);
// The vector xform approximates how the control points are transformed by the shader to
// more accurately compute how many *parametric* segments are needed.
// TODO: This doesn't account for perspective division yet, which will require updating the
// approximate transform based on each verb's control points' bounding box.
SkASSERT(params.transform().type() < Transform::Type::kProjection);
writer.setShaderTransform(wangs_formula::VectorXform{params.transform().matrix()},
params.transform().maxScaleFactor());
// TODO: Essentially the same as PathWedgeTessellator::write_patches but with a different
// PatchWriter template.
// For wedges, we iterate over each contour explicitly, using a fan point position that is in
// the midpoint of the current contour.
MidpointContourParser parser{path};
while (parser.parseNextContour()) {
writer.updateFanPointAttrib(parser.currentMidpoint());
SkPoint lastPoint = {0, 0};
SkPoint startPoint = {0, 0};
for (auto [verb, pts, w] : parser.currentContour()) {
switch (verb) {
case SkPathVerb::kMove:
startPoint = lastPoint = pts[0];
break;
case SkPathVerb::kLine:
// Unlike curve tessellation, wedges have to handle lines as part of the patch,
// effectively forming a single triangle with the fan point.
writer.writeLine(pts[0], pts[1]);
lastPoint = pts[1];
break;
case SkPathVerb::kQuad:
writer.writeQuadratic(pts);
lastPoint = pts[2];
break;
case SkPathVerb::kConic:
writer.writeConic(pts, *w);
lastPoint = pts[2];
break;
case SkPathVerb::kCubic:
writer.writeCubic(pts);
lastPoint = pts[3];
break;
default: break;
}
}
// Explicitly close the contour with another line segment, which also differs from curve
// tessellation since that approach's triangle step automatically closes the contour.
if (lastPoint != startPoint) {
writer.writeLine(lastPoint, startPoint);
}
}
}
void TessellateWedgesRenderStep::writeUniformsAndTextures(const DrawParams& params,
PipelineDataGatherer* gatherer) const {
SkDEBUGCODE(UniformExpectationsValidator uev(gatherer, this->uniforms());)
gatherer->write(params.transform().matrix());
}
} // namespace skgpu::graphite