Move Ganesh-specific bits of StrokeTessellator into an #ifdef

Bug: skia:12524
Change-Id: I31ddee40fed6e57caafe7a51fdd90459bd29cd6a
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/474356
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
This commit is contained in:
Chris Dalton 2021-11-19 19:02:53 -07:00 committed by SkCQ
parent 1ce70dc2ce
commit 5cf4ed47f4
7 changed files with 119 additions and 54 deletions

View File

@ -247,7 +247,7 @@ using PathStrokeList = StrokeTessellator::PathStrokeList;
using MakeTessellatorFn = std::unique_ptr<StrokeTessellator>(*)(PatchAttribs);
static std::unique_ptr<StrokeTessellator> make_hw_tessellator(PatchAttribs attribs) {
return std::make_unique<StrokeHardwareTessellator>(attribs);
return std::make_unique<StrokeHardwareTessellator>(attribs, 64);
}
static std::unique_ptr<StrokeTessellator> make_fixed_count_tessellator(PatchAttribs attribs) {

View File

@ -193,7 +193,8 @@ void StrokeTessellateOp::prePrepareTessellator(GrTessellationShader::ProgramArgs
if (can_use_hardware_tessellation(fTotalCombinedVerbCnt, *pipeline, caps)) {
// Only use hardware tessellation if we're drawing a somewhat large number of verbs.
// Otherwise we seem to be better off using instanced draws.
fTessellator = arena->make<StrokeHardwareTessellator>(fPatchAttribs);
fTessellator = arena->make<StrokeHardwareTessellator>(
fPatchAttribs, caps.shaderCaps()->maxTessellationSegments());
shaderMode = GrStrokeTessellationShader::Mode::kHardwareTessellation;
// This sets a limit on the number of binary search iterations inside the shader, so we
// round up to the next log2 to guarantee it makes enough.

View File

@ -8,8 +8,6 @@
#include "src/gpu/tessellate/StrokeFixedCountTessellator.h"
#include "src/core/SkGeometry.h"
#include "src/gpu/GrMeshDrawTarget.h"
#include "src/gpu/GrResourceProvider.h"
#include "src/gpu/geometry/GrPathUtils.h"
#include "src/gpu/tessellate/PatchWriter.h"
#include "src/gpu/tessellate/StrokeIterator.h"
@ -17,7 +15,9 @@
#include "src/gpu/tessellate/shaders/GrTessellationShader.h"
#if SK_GPU_V1
#include "src/gpu/GrMeshDrawTarget.h"
#include "src/gpu/GrOpFlushState.h"
#include "src/gpu/GrResourceProvider.h"
#endif
namespace skgpu {
@ -190,23 +190,22 @@ int worst_case_edges_in_join(SkPaint::Join joinType, float numRadialSegmentsPerR
} // namespace
GR_DECLARE_STATIC_UNIQUE_KEY(gVertexIDFallbackBufferKey);
int StrokeFixedCountTessellator::prepare(GrMeshDrawTarget* target,
const SkMatrix& shaderMatrix,
std::array<float,2> matrixMinMaxScales,
PathStrokeList* pathStrokeList,
int totalCombinedVerbCnt) {
int maxEdgesInJoin = 0;
float maxRadialSegmentsPerRadian = 0;
int StrokeFixedCountTessellator::patchPreallocCount(int totalCombinedStrokeVerbCnt) const {
// Over-allocate enough patches for each stroke to chop once, and for 8 extra caps. Since we
// have to chop at inflections, points of 180 degree rotation, and anywhere a stroke requires
// too many parametric segments, many strokes will end up getting choppped.
int strokePreallocCount = totalCombinedVerbCnt * 2;
int strokePreallocCount = totalCombinedStrokeVerbCnt * 2;
int capPreallocCount = 8;
int minInstancesPerChunk = strokePreallocCount + capPreallocCount;
PatchWriter patchWriter(target, this, minInstancesPerChunk);
return strokePreallocCount + capPreallocCount;
}
int StrokeFixedCountTessellator::writePatches(PatchWriter& patchWriter,
const SkMatrix& shaderMatrix,
std::array<float,2> matrixMinMaxScales,
PathStrokeList* pathStrokeList) {
int maxEdgesInJoin = 0;
float maxRadialSegmentsPerRadian = 0;
InstanceWriter instanceWriter(patchWriter, matrixMinMaxScales[1]);
if (!(fAttribs & PatchAttribs::kStrokeParams)) {
@ -352,7 +351,33 @@ int StrokeFixedCountTessellator::prepare(GrMeshDrawTarget* target,
// number of edges in an instance is the sum of edges from the join and stroke sections both.
// NOTE: The final join edge and the first stroke edge are co-located, however we still need to
// emit both because the join's edge is half-width and the stroke's is full-width.
fFixedEdgeCount = maxEdgesInJoin + maxEdgesInStroke;
return maxEdgesInJoin + maxEdgesInStroke;
}
void StrokeFixedCountTessellator::InitializeVertexIDFallbackBuffer(VertexWriter vertexWriter,
size_t bufferSize) {
SkASSERT(bufferSize % (sizeof(float) * 2) == 0);
int edgeCount = bufferSize / (sizeof(float) * 2);
for (int i = 0; i < edgeCount; ++i) {
vertexWriter << (float)i << (float)-i;
}
}
#if SK_GPU_V1
GR_DECLARE_STATIC_UNIQUE_KEY(gVertexIDFallbackBufferKey);
int StrokeFixedCountTessellator::prepare(GrMeshDrawTarget* target,
const SkMatrix& shaderMatrix,
std::array<float,2> matrixMinMaxScales,
PathStrokeList* pathStrokeList,
int totalCombinedStrokeVerbCnt) {
PatchWriter patchWriter(target, this, this->patchPreallocCount(totalCombinedStrokeVerbCnt));
fFixedEdgeCount = this->writePatches(patchWriter,
shaderMatrix,
matrixMinMaxScales,
pathStrokeList);
// Don't draw more vertices than can be indexed by a signed short. We just have to draw the line
// somewhere and this seems reasonable enough. (There are two vertices per edge, so 2^14 edges
@ -377,16 +402,6 @@ int StrokeFixedCountTessellator::prepare(GrMeshDrawTarget* target,
return fFixedEdgeCount;
}
void StrokeFixedCountTessellator::InitializeVertexIDFallbackBuffer(VertexWriter vertexWriter,
size_t bufferSize) {
SkASSERT(bufferSize % (sizeof(float) * 2) == 0);
int edgeCount = bufferSize / (sizeof(float) * 2);
for (int i = 0; i < edgeCount; ++i) {
vertexWriter << (float)i << (float)-i;
}
}
#if SK_GPU_V1
void StrokeFixedCountTessellator::draw(GrOpFlushState* flushState) const {
if (fVertexChunkArray.empty() || fFixedEdgeCount <= 0) {
return;
@ -403,6 +418,7 @@ void StrokeFixedCountTessellator::draw(GrOpFlushState* flushState) const {
0);
}
}
#endif
} // namespace skgpu

View File

@ -16,21 +16,28 @@ namespace skgpu {
// Renders strokes as fixed-count triangle strip instances. Any extra triangles not needed by the
// instance are emitted as degenerate triangles.
class StrokeFixedCountTessellator : public StrokeTessellator {
class StrokeFixedCountTessellator final : public StrokeTessellator {
public:
constexpr static float kMaxParametricSegments_pow4 = 32*32*32*32; // 32^4
constexpr static int8_t kMaxParametricSegments_log2 = 5; // log2(32)
StrokeFixedCountTessellator(PatchAttribs attribs) : StrokeTessellator(attribs) {}
int patchPreallocCount(int totalCombinedStrokeVerbCnt) const final;
int writePatches(PatchWriter&,
const SkMatrix& shaderMatrix,
std::array<float,2> matrixMinMaxScales,
PathStrokeList*) final;
#if SK_GPU_V1
int prepare(GrMeshDrawTarget*,
const SkMatrix& shaderMatrix,
std::array<float,2> matrixMinMaxScales,
PathStrokeList*,
int totalCombinedVerbCnt) override;
int totalCombinedStrokeVerbCnt) final;
#if SK_GPU_V1
void draw(GrOpFlushState*) const override;
void draw(GrOpFlushState*) const final;
#endif
// Initializes the fallback vertex buffer that should be bound when sk_VertexID is not
@ -67,10 +74,12 @@ public:
}
private:
#if SK_GPU_V1
int fFixedEdgeCount = 0;
// Only used if sk_VertexID is not supported.
sk_sp<const GrGpuBuffer> fVertexBufferIfNoIDSupport;
#endif
};
} // namespace skgpu

View File

@ -8,14 +8,13 @@
#include "src/gpu/tessellate/StrokeHardwareTessellator.h"
#include "src/core/SkPathPriv.h"
#include "src/gpu/GrMeshDrawTarget.h"
#include "src/gpu/GrRecordingContextPriv.h"
#include "src/gpu/geometry/GrPathUtils.h"
#include "src/gpu/tessellate/PatchWriter.h"
#include "src/gpu/tessellate/WangsFormula.h"
#include "src/gpu/tessellate/shaders/GrTessellationShader.h"
#if SK_GPU_V1
#include "src/gpu/GrMeshDrawTarget.h"
#include "src/gpu/GrOpFlushState.h"
#endif
@ -56,11 +55,11 @@ public:
kBowtie = SkPaint::kLast_Join + 1 // Double sided round join.
};
HwPatchWriter(PatchWriter& patchWriter, const GrShaderCaps& shaderCaps, float matrixMaxScale)
HwPatchWriter(PatchWriter& patchWriter, int maxTessellationSegments, float matrixMaxScale)
: fPatchWriter(patchWriter)
// Subtract 2 because the tessellation shader chops every cubic at two locations, and
// each chop has the potential to introduce an extra segment.
, fMaxTessellationSegments(shaderCaps.maxTessellationSegments() - 2)
, fMaxTessellationSegments(std::max(maxTessellationSegments - 2, 1))
, fParametricPrecision(StrokeTolerances::CalcParametricPrecision(matrixMaxScale)) {
}
@ -664,19 +663,20 @@ SK_ALWAYS_INLINE bool cubic_has_cusp(const SkPoint p[4]) {
} // namespace
int StrokeHardwareTessellator::prepare(GrMeshDrawTarget* target,
const SkMatrix& shaderMatrix,
std::array<float,2> matrixMinMaxScales,
PathStrokeList* pathStrokeList,
int totalCombinedVerbCnt) {
int StrokeHardwareTessellator::patchPreallocCount(int totalCombinedStrokeVerbCnt) const {
// Over-allocate enough patches for 1 in 4 strokes to chop and for 8 extra caps.
int strokePreallocCount = (totalCombinedStrokeVerbCnt * 5) / 4;
int capPreallocCount = 8;
return strokePreallocCount + capPreallocCount;
}
int StrokeHardwareTessellator::writePatches(PatchWriter& patchWriter,
const SkMatrix& shaderMatrix,
std::array<float,2> matrixMinMaxScales,
PathStrokeList* pathStrokeList) {
using JoinType = HwPatchWriter::JoinType;
// Over-allocate enough patches for 1 in 4 strokes to chop and for 8 extra caps.
int strokePreallocCount = totalCombinedVerbCnt * 5/4;
int capPreallocCount = 8;
int minPatchesPerChunk = strokePreallocCount + capPreallocCount;
PatchWriter patchWriter(target, this, minPatchesPerChunk);
HwPatchWriter hwPatchWriter(patchWriter, *target->caps().shaderCaps(), matrixMinMaxScales[1]);
HwPatchWriter hwPatchWriter(patchWriter, fMaxTessellationSegments, matrixMinMaxScales[1]);
if (!(fAttribs & PatchAttribs::kStrokeParams)) {
// Strokes are static. Calculate tolerances once.
@ -851,12 +851,23 @@ int StrokeHardwareTessellator::prepare(GrMeshDrawTarget* target,
}
#if SK_GPU_V1
int StrokeHardwareTessellator::prepare(GrMeshDrawTarget* target,
const SkMatrix& shaderMatrix,
std::array<float,2> matrixMinMaxScales,
PathStrokeList* pathStrokeList,
int totalCombinedStrokeVerbCnt) {
PatchWriter patchWriter(target, this, this->patchPreallocCount(totalCombinedStrokeVerbCnt));
return this->writePatches(patchWriter, shaderMatrix, matrixMinMaxScales, pathStrokeList);
}
void StrokeHardwareTessellator::draw(GrOpFlushState* flushState) const {
for (const auto& vertexChunk : fVertexChunkArray) {
flushState->bindBuffers(nullptr, nullptr, vertexChunk.fBuffer);
flushState->draw(vertexChunk.fCount, vertexChunk.fBase);
}
}
#endif
} // namespace skgpu

View File

@ -16,18 +16,30 @@ namespace skgpu {
// Renders opaque, constant-color strokes by decomposing them into standalone tessellation patches.
// Each patch is either a "cubic" (single stroked bezier curve with butt caps) or a "join". Requires
// MSAA if antialiasing is desired.
class StrokeHardwareTessellator : public StrokeTessellator {
class StrokeHardwareTessellator final : public StrokeTessellator {
public:
StrokeHardwareTessellator(PatchAttribs attribs) : StrokeTessellator(attribs) {}
StrokeHardwareTessellator(PatchAttribs attribs, int maxTessellationSegments)
: StrokeTessellator(attribs), fMaxTessellationSegments(maxTessellationSegments) {}
int patchPreallocCount(int totalCombinedStrokeVerbCnt) const final;
int writePatches(PatchWriter&,
const SkMatrix& shaderMatrix,
std::array<float,2> matrixMinMaxScales,
PathStrokeList*) final;
#if SK_GPU_V1
int prepare(GrMeshDrawTarget*,
const SkMatrix& shaderMatrix,
std::array<float,2> matrixMinMaxScales,
PathStrokeList*,
int totalCombinedVerbCnt) override;
#if SK_GPU_V1
void draw(GrOpFlushState*) const override;
int totalCombinedStrokeVerbCnt) final;
void draw(GrOpFlushState*) const final;
#endif
private:
const int fMaxTessellationSegments;
};
} // namespace skgpu

View File

@ -22,6 +22,8 @@ class GrOpFlushState;
namespace skgpu {
class PatchWriter;
// Prepares GPU data for, and then draws a stroke's tessellated geometry.
class StrokeTessellator {
public:
@ -36,6 +38,21 @@ public:
StrokeTessellator(PatchAttribs attribs) : fAttribs(attribs) {}
// Gives an approximate initial buffer size for this class to write patches into. Ideally the
// whole stroke will fit into this initial buffer, but if it requires a lot of chopping, the
// PatchWriter will allocate more buffer(s).
virtual int patchPreallocCount(int totalCombinedStrokeVerbCnt) const = 0;
// Writes out patches to the given PatchWriter, chopping as necessary.
//
// Returns the fixed number of edges the tessellator will draw per patch, if using fixed-count
// rendering, otherwise 0.
virtual int writePatches(PatchWriter&,
const SkMatrix& shaderMatrix,
std::array<float,2> matrixMinMaxScales,
PathStrokeList*) = 0;
#if SK_GPU_V1
// Called before draw(). Prepares GPU buffers containing the geometry to tessellate.
//
// Returns the fixed number of edges the tessellator will draw per patch, if using fixed-count
@ -44,9 +61,8 @@ public:
const SkMatrix& shaderMatrix,
std::array<float,2> matrixMinMaxScales,
PathStrokeList*,
int totalCombinedVerbCnt) = 0;
int totalCombinedStrokeVerbCnt) = 0;
#if SK_GPU_V1
// Issues draw calls for the tessellated stroke. The caller is responsible for creating and
// binding a pipeline that uses this class's shader() before calling draw().
virtual void draw(GrOpFlushState*) const = 0;