Yank GrStrokeTessellationShader out of StrokeTessellators
Bug: skia:12524 Change-Id: I208f257bca46c8fcb15cf58baea4c3e058310327 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/471216 Commit-Queue: Chris Dalton <csmartdalton@google.com> Reviewed-by: Greg Daniel <egdaniel@google.com>
This commit is contained in:
parent
b339d59fdb
commit
9aca7f7dd0
@ -244,30 +244,14 @@ DEF_PATH_TESS_BENCH(middle_out_triangulation,
|
||||
}
|
||||
|
||||
using PathStrokeList = StrokeTessellator::PathStrokeList;
|
||||
using MakeTessellatorFn = std::unique_ptr<StrokeTessellator>(*)(PatchAttribs,
|
||||
const GrShaderCaps&,
|
||||
const SkMatrix&,
|
||||
PathStrokeList*,
|
||||
std::array<float, 2>);
|
||||
using MakeTessellatorFn = std::unique_ptr<StrokeTessellator>(*)(PatchAttribs);
|
||||
|
||||
static std::unique_ptr<StrokeTessellator> make_hw_tessellator(
|
||||
PatchAttribs attribs,
|
||||
const GrShaderCaps& shaderCaps,
|
||||
const SkMatrix& viewMatrix,
|
||||
PathStrokeList* pathStrokeList,
|
||||
std::array<float,2> matrixMinMaxScales) {
|
||||
return std::make_unique<StrokeHardwareTessellator>(shaderCaps, attribs, viewMatrix,
|
||||
pathStrokeList, matrixMinMaxScales);
|
||||
static std::unique_ptr<StrokeTessellator> make_hw_tessellator(PatchAttribs attribs) {
|
||||
return std::make_unique<StrokeHardwareTessellator>(attribs);
|
||||
}
|
||||
|
||||
static std::unique_ptr<StrokeTessellator> make_fixed_count_tessellator(
|
||||
PatchAttribs attribs,
|
||||
const GrShaderCaps& shaderCaps,
|
||||
const SkMatrix& viewMatrix,
|
||||
PathStrokeList* pathStrokeList,
|
||||
std::array<float, 2> matrixMinMaxScales) {
|
||||
return std::make_unique<StrokeFixedCountTessellator>(shaderCaps, attribs, viewMatrix,
|
||||
pathStrokeList, matrixMinMaxScales);
|
||||
static std::unique_ptr<StrokeTessellator> make_fixed_count_tessellator(PatchAttribs attribs) {
|
||||
return std::make_unique<StrokeFixedCountTessellator>(attribs);
|
||||
}
|
||||
|
||||
using MakePathStrokesFn = std::vector<PathStrokeList>(*)();
|
||||
@ -367,14 +351,16 @@ private:
|
||||
fTotalVerbCount += fPathStrokes[i].fPath.countVerbs();
|
||||
}
|
||||
|
||||
fTessellator = fMakeTessellatorFn(fPatchAttribs, *fTarget->caps().shaderCaps(),
|
||||
SkMatrix::Scale(fMatrixScale, fMatrixScale),
|
||||
fPathStrokes.data(), {fMatrixScale, fMatrixScale});
|
||||
fTessellator = fMakeTessellatorFn(fPatchAttribs);
|
||||
}
|
||||
|
||||
void onDraw(int loops, SkCanvas*) final {
|
||||
for (int i = 0; i < loops; ++i) {
|
||||
fTessellator->prepare(fTarget.get(), fTotalVerbCount);
|
||||
fTessellator->prepare(fTarget.get(),
|
||||
SkMatrix::Scale(fMatrixScale, fMatrixScale),
|
||||
{fMatrixScale, fMatrixScale},
|
||||
fPathStrokes.data(),
|
||||
fTotalVerbCount);
|
||||
fTarget->resetAllocator();
|
||||
}
|
||||
}
|
||||
|
@ -7,15 +7,14 @@
|
||||
|
||||
#include "src/gpu/ops/StrokeTessellateOp.h"
|
||||
|
||||
#include "src/core/SkMathPriv.h"
|
||||
#include "src/core/SkPathPriv.h"
|
||||
#include "src/gpu/GrAppliedClip.h"
|
||||
#include "src/gpu/GrOpFlushState.h"
|
||||
#include "src/gpu/GrRecordingContextPriv.h"
|
||||
#include "src/gpu/tessellate/StrokeFixedCountTessellator.h"
|
||||
#include "src/gpu/tessellate/StrokeHardwareTessellator.h"
|
||||
#include "src/gpu/tessellate/shaders/GrTessellationShader.h"
|
||||
|
||||
using DynamicStroke = GrStrokeTessellationShader::DynamicStroke;
|
||||
#include "src/gpu/tessellate/shaders/GrStrokeTessellationShader.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@ -114,7 +113,7 @@ GrOp::CombineResult StrokeTessellateOp::onCombineIfPossible(GrOp* grOp, SkArenaA
|
||||
|
||||
auto combinedAttribs = fPatchAttribs | op->fPatchAttribs;
|
||||
if (!(combinedAttribs & PatchAttribs::kStrokeParams) &&
|
||||
!DynamicStroke::StrokesHaveEqualDynamicState(this->headStroke(), op->headStroke())) {
|
||||
!StrokeParams::StrokesHaveEqualParams(this->headStroke(), op->headStroke())) {
|
||||
// The paths have different stroke properties. We will need to enable dynamic stroke if we
|
||||
// still decide to combine them.
|
||||
if (this->headStroke().isHairlineStyle()) {
|
||||
@ -186,39 +185,42 @@ void StrokeTessellateOp::prePrepareTessellator(GrTessellationShader::ProgramArgs
|
||||
const GrCaps& caps = *args.fCaps;
|
||||
SkArenaAlloc* arena = args.fArena;
|
||||
|
||||
std::array<float, 2> matrixMinMaxScales;
|
||||
if (!fViewMatrix.getMinMaxScales(matrixMinMaxScales.data())) {
|
||||
matrixMinMaxScales.fill(1);
|
||||
}
|
||||
|
||||
auto* pipeline = GrTessellationShader::MakePipeline(args, fAAType, std::move(clip),
|
||||
std::move(fProcessors));
|
||||
|
||||
GrStrokeTessellationShader::Mode shaderMode;
|
||||
int maxParametricSegments_log2;
|
||||
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>(*caps.shaderCaps(),
|
||||
fPatchAttribs,
|
||||
fViewMatrix,
|
||||
&fPathStrokeList,
|
||||
matrixMinMaxScales);
|
||||
fTessellator = arena->make<StrokeHardwareTessellator>(fPatchAttribs);
|
||||
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.
|
||||
maxParametricSegments_log2 = SkNextLog2(caps.shaderCaps()->maxTessellationSegments());
|
||||
} else {
|
||||
fTessellator = arena->make<StrokeFixedCountTessellator>(*caps.shaderCaps(),
|
||||
fPatchAttribs,
|
||||
fViewMatrix,
|
||||
&fPathStrokeList,
|
||||
matrixMinMaxScales);
|
||||
fTessellator = arena->make<StrokeFixedCountTessellator>(fPatchAttribs);
|
||||
shaderMode = GrStrokeTessellationShader::Mode::kFixedCount;
|
||||
maxParametricSegments_log2 = StrokeFixedCountTessellator::kMaxParametricSegments_log2;
|
||||
}
|
||||
|
||||
fTessellationShader = args.fArena->make<GrStrokeTessellationShader>(*caps.shaderCaps(),
|
||||
shaderMode,
|
||||
fPatchAttribs,
|
||||
fViewMatrix,
|
||||
this->headStroke(),
|
||||
this->headColor(),
|
||||
maxParametricSegments_log2);
|
||||
|
||||
auto fillStencil = &GrUserStencilSettings::kUnused;
|
||||
if (fNeedsStencil) {
|
||||
fStencilProgram = GrTessellationShader::MakeProgram(args, fTessellator->shader(), pipeline,
|
||||
fStencilProgram = GrTessellationShader::MakeProgram(args, fTessellationShader, pipeline,
|
||||
&kMarkStencil);
|
||||
fillStencil = &kTestAndResetStencil;
|
||||
args.fXferBarrierFlags = GrXferBarrierFlags::kNone;
|
||||
}
|
||||
|
||||
fFillProgram = GrTessellationShader::MakeProgram(args, fTessellator->shader(), pipeline,
|
||||
fFillProgram = GrTessellationShader::MakeProgram(args, fTessellationShader, pipeline,
|
||||
fillStencil);
|
||||
}
|
||||
|
||||
@ -249,7 +251,18 @@ void StrokeTessellateOp::onPrepare(GrOpFlushState* flushState) {
|
||||
&flushState->caps()}, flushState->detachAppliedClip());
|
||||
}
|
||||
SkASSERT(fTessellator);
|
||||
fTessellator->prepare(flushState, fTotalCombinedVerbCnt);
|
||||
std::array<float, 2> matrixMinMaxScales;
|
||||
if (!fViewMatrix.getMinMaxScales(matrixMinMaxScales.data())) {
|
||||
matrixMinMaxScales.fill(1);
|
||||
}
|
||||
int fixedEdgeCount = fTessellator->prepare(flushState,
|
||||
fViewMatrix,
|
||||
matrixMinMaxScales,
|
||||
&fPathStrokeList,
|
||||
fTotalCombinedVerbCnt);
|
||||
if (!fTessellationShader->willUseTessellationShaders()) {
|
||||
fTessellationShader->setFixedCountNumTotalEdges(fixedEdgeCount);
|
||||
}
|
||||
}
|
||||
|
||||
void StrokeTessellateOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "src/gpu/tessellate/shaders/GrTessellationShader.h"
|
||||
|
||||
class GrRecordingContext;
|
||||
class GrStrokeTessellationShader;
|
||||
|
||||
namespace skgpu::v1 {
|
||||
|
||||
@ -73,6 +74,7 @@ private:
|
||||
bool fNeedsStencil;
|
||||
|
||||
StrokeTessellator* fTessellator = nullptr;
|
||||
GrStrokeTessellationShader* fTessellationShader;
|
||||
const GrProgramInfo* fStencilProgram = nullptr; // Only used if the stroke has transparency.
|
||||
const GrProgramInfo* fFillProgram = nullptr;
|
||||
};
|
||||
|
@ -20,6 +20,7 @@ PatchWriter::PatchWriter(GrMeshDrawTarget* target,
|
||||
: PatchWriter(target,
|
||||
&tessellator->fVertexChunkArray,
|
||||
tessellator->fAttribs,
|
||||
sizeof(SkPoint) * 4 + PatchAttribsStride(tessellator->fAttribs),
|
||||
initialPatchAllocCount) {
|
||||
}
|
||||
#endif
|
||||
|
@ -25,9 +25,10 @@ public:
|
||||
PatchWriter(GrMeshDrawTarget* target,
|
||||
GrVertexChunkArray* vertexChunkArray,
|
||||
PatchAttribs attribs,
|
||||
size_t patchStride,
|
||||
int initialAllocCount)
|
||||
: fPatchAttribs(attribs)
|
||||
, fChunker(target, vertexChunkArray, PatchStride(fPatchAttribs), initialAllocCount) {
|
||||
, fChunker(target, vertexChunkArray, patchStride, initialAllocCount) {
|
||||
}
|
||||
|
||||
#if SK_GPU_V1
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "src/gpu/geometry/GrPathUtils.h"
|
||||
#include "src/gpu/tessellate/StrokeIterator.h"
|
||||
#include "src/gpu/tessellate/WangsFormula.h"
|
||||
#include "src/gpu/tessellate/shaders/GrTessellationShader.h"
|
||||
|
||||
#if SK_GPU_V1
|
||||
#include "src/gpu/GrOpFlushState.h"
|
||||
@ -22,8 +23,8 @@ namespace skgpu {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr static float kMaxParametricSegments_pow4 = 32*32*32*32; // 32^4
|
||||
constexpr static int8_t kMaxParametricSegments_log2 = 5; // log2(32)
|
||||
constexpr static float kMaxParametricSegments_pow4 =
|
||||
StrokeFixedCountTessellator::kMaxParametricSegments_pow4;
|
||||
|
||||
// Writes out strokes to the given instance chunk array, chopping if necessary so that all instances
|
||||
// require 32 parametric segments or less. (We don't consider radial segments here. The tessellator
|
||||
@ -215,13 +216,13 @@ private:
|
||||
bool fHasLastControlPoint = false;
|
||||
|
||||
// Values for the current dynamic state (if any) that will get written out with each instance.
|
||||
GrStrokeTessellationShader::DynamicStroke fDynamicStroke;
|
||||
StrokeParams fDynamicStroke;
|
||||
GrVertexColor fDynamicColor;
|
||||
};
|
||||
|
||||
// Returns the worst-case number of edges we will need in order to draw a join of the given type.
|
||||
int worst_case_edges_in_join(SkPaint::Join joinType, float numRadialSegmentsPerRadian) {
|
||||
int numEdges = GrStrokeTessellationShader::NumFixedEdgesInJoin(joinType);
|
||||
int numEdges = StrokeFixedCountTessellator::NumFixedEdgesInJoin(joinType);
|
||||
if (joinType == SkPaint::kRound_Join) {
|
||||
// For round joins we need to count the radial edges on our own. Account for a worst-case
|
||||
// join of 180 degrees (SK_ScalarPI radians).
|
||||
@ -233,23 +234,13 @@ int worst_case_edges_in_join(SkPaint::Join joinType, float numRadialSegmentsPerR
|
||||
} // namespace
|
||||
|
||||
|
||||
StrokeFixedCountTessellator::StrokeFixedCountTessellator(const GrShaderCaps& shaderCaps,
|
||||
PatchAttribs attribs,
|
||||
const SkMatrix& viewMatrix,
|
||||
PathStrokeList* pathStrokeList,
|
||||
std::array<float,2> matrixMinMaxScales)
|
||||
: StrokeTessellator(shaderCaps,
|
||||
GrStrokeTessellationShader::Mode::kFixedCount,
|
||||
attribs,
|
||||
kMaxParametricSegments_log2,
|
||||
viewMatrix,
|
||||
pathStrokeList,
|
||||
matrixMinMaxScales) {
|
||||
}
|
||||
|
||||
GR_DECLARE_STATIC_UNIQUE_KEY(gVertexIDFallbackBufferKey);
|
||||
|
||||
void StrokeFixedCountTessellator::prepare(GrMeshDrawTarget* target, int totalCombinedVerbCnt) {
|
||||
int StrokeFixedCountTessellator::prepare(GrMeshDrawTarget* target,
|
||||
const SkMatrix& shaderMatrix,
|
||||
std::array<float,2> matrixMinMaxScales,
|
||||
PathStrokeList* pathStrokeList,
|
||||
int totalCombinedVerbCnt) {
|
||||
int maxEdgesInJoin = 0;
|
||||
float maxRadialSegmentsPerRadian = 0;
|
||||
|
||||
@ -261,16 +252,16 @@ void StrokeFixedCountTessellator::prepare(GrMeshDrawTarget* target, int totalCom
|
||||
int minInstancesPerChunk = strokePreallocCount + capPreallocCount;
|
||||
InstanceWriter instanceWriter(fAttribs,
|
||||
target,
|
||||
fMatrixMinMaxScales[1],
|
||||
fShader.viewMatrix(),
|
||||
matrixMinMaxScales[1],
|
||||
shaderMatrix,
|
||||
&fInstanceChunks,
|
||||
fShader.instanceStride(),
|
||||
sizeof(SkPoint) * 5 + PatchAttribsStride(fAttribs),
|
||||
minInstancesPerChunk);
|
||||
|
||||
if (!fShader.hasDynamicStroke()) {
|
||||
if (!(fAttribs & PatchAttribs::kStrokeParams)) {
|
||||
// Strokes are static. Calculate tolerances once.
|
||||
const SkStrokeRec& stroke = fPathStrokeList->fStroke;
|
||||
float localStrokeWidth = StrokeTolerances::GetLocalStrokeWidth(fMatrixMinMaxScales.data(),
|
||||
const SkStrokeRec& stroke = pathStrokeList->fStroke;
|
||||
float localStrokeWidth = StrokeTolerances::GetLocalStrokeWidth(matrixMinMaxScales.data(),
|
||||
stroke.getWidth());
|
||||
float numRadialSegmentsPerRadian = StrokeTolerances::CalcNumRadialSegmentsPerRadian(
|
||||
instanceWriter.parametricPrecision(), localStrokeWidth);
|
||||
@ -282,9 +273,9 @@ void StrokeFixedCountTessellator::prepare(GrMeshDrawTarget* target, int totalCom
|
||||
// have dynamic stroke.
|
||||
StrokeToleranceBuffer toleranceBuffer(instanceWriter.parametricPrecision());
|
||||
|
||||
for (PathStrokeList* pathStroke = fPathStrokeList; pathStroke; pathStroke = pathStroke->fNext) {
|
||||
for (PathStrokeList* pathStroke = pathStrokeList; pathStroke; pathStroke = pathStroke->fNext) {
|
||||
const SkStrokeRec& stroke = pathStroke->fStroke;
|
||||
if (fShader.hasDynamicStroke()) {
|
||||
if (fAttribs & PatchAttribs::kStrokeParams) {
|
||||
// Strokes are dynamic. Calculate tolerances every time.
|
||||
float numRadialSegmentsPerRadian =
|
||||
toleranceBuffer.fetchRadialSegmentsPerRadian(pathStroke);
|
||||
@ -295,10 +286,10 @@ void StrokeFixedCountTessellator::prepare(GrMeshDrawTarget* target, int totalCom
|
||||
maxRadialSegmentsPerRadian);
|
||||
instanceWriter.updateDynamicStroke(stroke);
|
||||
}
|
||||
if (fShader.hasDynamicColor()) {
|
||||
if (fAttribs & PatchAttribs::kColor) {
|
||||
instanceWriter.updateDynamicColor(pathStroke->fColor);
|
||||
}
|
||||
StrokeIterator strokeIter(pathStroke->fPath, &pathStroke->fStroke, &fShader.viewMatrix());
|
||||
StrokeIterator strokeIter(pathStroke->fPath, &pathStroke->fStroke, &shaderMatrix);
|
||||
while (strokeIter.next()) {
|
||||
const SkPoint* p = strokeIter.pts();
|
||||
switch (strokeIter.verb()) {
|
||||
@ -410,18 +401,18 @@ void StrokeFixedCountTessellator::prepare(GrMeshDrawTarget* target, int totalCom
|
||||
// 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.
|
||||
int fixedEdgeCount = maxEdgesInJoin + maxEdgesInStroke;
|
||||
fFixedEdgeCount = maxEdgesInJoin + maxEdgesInStroke;
|
||||
|
||||
// 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
|
||||
// make 2^15 vertices.)
|
||||
fixedEdgeCount = std::min(fixedEdgeCount, (1 << 14) - 1);
|
||||
fFixedEdgeCount = std::min(fFixedEdgeCount, (1 << 14) - 1);
|
||||
|
||||
if (!target->caps().shaderCaps()->vertexIDSupport()) {
|
||||
// Our shader won't be able to use sk_VertexID. Bind a fallback vertex buffer with the IDs
|
||||
// in it instead.
|
||||
constexpr static int kMaxVerticesInFallbackBuffer = 2048;
|
||||
fixedEdgeCount = std::min(fixedEdgeCount, kMaxVerticesInFallbackBuffer/2);
|
||||
fFixedEdgeCount = std::min(fFixedEdgeCount, kMaxVerticesInFallbackBuffer/2);
|
||||
|
||||
GR_DEFINE_STATIC_UNIQUE_KEY(gVertexIDFallbackBufferKey);
|
||||
|
||||
@ -429,16 +420,24 @@ void StrokeFixedCountTessellator::prepare(GrMeshDrawTarget* target, int totalCom
|
||||
GrGpuBufferType::kVertex,
|
||||
kMaxVerticesInFallbackBuffer * sizeof(float),
|
||||
gVertexIDFallbackBufferKey,
|
||||
GrStrokeTessellationShader::InitializeVertexIDFallbackBuffer);
|
||||
InitializeVertexIDFallbackBuffer);
|
||||
}
|
||||
|
||||
fShader.setFixedCountNumTotalEdges(fixedEdgeCount);
|
||||
fFixedVertexCount = fixedEdgeCount * 2;
|
||||
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 (fInstanceChunks.empty() || fFixedVertexCount <= 0) {
|
||||
if (fInstanceChunks.empty() || fFixedEdgeCount <= 0) {
|
||||
return;
|
||||
}
|
||||
if (!flushState->caps().shaderCaps()->vertexIDSupport() &&
|
||||
@ -447,7 +446,10 @@ void StrokeFixedCountTessellator::draw(GrOpFlushState* flushState) const {
|
||||
}
|
||||
for (const auto& instanceChunk : fInstanceChunks) {
|
||||
flushState->bindBuffers(nullptr, instanceChunk.fBuffer, fVertexBufferIfNoIDSupport);
|
||||
flushState->drawInstanced(instanceChunk.fCount, instanceChunk.fBase, fFixedVertexCount, 0);
|
||||
flushState->drawInstanced(instanceChunk.fCount,
|
||||
instanceChunk.fBase,
|
||||
fFixedEdgeCount * 2,
|
||||
0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -8,6 +8,7 @@
|
||||
#ifndef tessellate_StrokeFixedCountTessellator_DEFINED
|
||||
#define tessellate_StrokeFixedCountTessellator_DEFINED
|
||||
|
||||
#include "src/gpu/GrGpuBuffer.h"
|
||||
#include "src/gpu/GrVertexChunkArray.h"
|
||||
#include "src/gpu/tessellate/StrokeTessellator.h"
|
||||
|
||||
@ -17,20 +18,57 @@ namespace skgpu {
|
||||
// instance are emitted as degenerate triangles.
|
||||
class StrokeFixedCountTessellator : public StrokeTessellator {
|
||||
public:
|
||||
StrokeFixedCountTessellator(const GrShaderCaps&,
|
||||
PatchAttribs,
|
||||
const SkMatrix&,
|
||||
PathStrokeList*,
|
||||
std::array<float, 2> matrixMinMaxScales);
|
||||
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 prepare(GrMeshDrawTarget*,
|
||||
const SkMatrix& shaderMatrix,
|
||||
std::array<float,2> matrixMinMaxScales,
|
||||
PathStrokeList*,
|
||||
int totalCombinedVerbCnt) override;
|
||||
|
||||
void prepare(GrMeshDrawTarget*, int totalCombinedVerbCnt) override;
|
||||
#if SK_GPU_V1
|
||||
void draw(GrOpFlushState*) const override;
|
||||
#endif
|
||||
|
||||
// Initializes the fallback vertex buffer that should be bound when sk_VertexID is not
|
||||
// supported. Each vertex is a single float and each edge is composed of two vertices, so the
|
||||
// desired edge count in the buffer is presumed to be "bufferSize / (sizeof(float) * 2)". The
|
||||
// caller cannot draw more vertices than edgeCount * 2.
|
||||
static void InitializeVertexIDFallbackBuffer(VertexWriter vertexWriter, size_t bufferSize);
|
||||
|
||||
// Returns the fixed number of edges that are always emitted with the given join type. If the
|
||||
// join is round, the caller needs to account for the additional radial edges on their own.
|
||||
// Specifically, each join always emits:
|
||||
//
|
||||
// * Two colocated edges at the beginning (a full-width edge to seam with the preceding stroke
|
||||
// and a half-width edge to begin the join).
|
||||
//
|
||||
// * An extra edge in the middle for miter joins, or else a variable number of radial edges
|
||||
// for round joins (the caller is responsible for counting radial edges from round joins).
|
||||
//
|
||||
// * A half-width edge at the end of the join that will be colocated with the first
|
||||
// (full-width) edge of the stroke.
|
||||
//
|
||||
constexpr static int NumFixedEdgesInJoin(SkPaint::Join joinType) {
|
||||
switch (joinType) {
|
||||
case SkPaint::kMiter_Join:
|
||||
return 4;
|
||||
case SkPaint::kRound_Join:
|
||||
// The caller is responsible for counting the variable number of middle, radial
|
||||
// segments on round joins.
|
||||
[[fallthrough]];
|
||||
case SkPaint::kBevel_Join:
|
||||
return 3;
|
||||
}
|
||||
SkUNREACHABLE;
|
||||
}
|
||||
|
||||
private:
|
||||
GrVertexChunkArray fInstanceChunks;
|
||||
int fFixedVertexCount = 0;
|
||||
int fFixedEdgeCount = 0;
|
||||
|
||||
// Only used if sk_VertexID is not supported.
|
||||
sk_sp<const GrGpuBuffer> fVertexBufferIfNoIDSupport;
|
||||
|
@ -7,12 +7,12 @@
|
||||
|
||||
#include "src/gpu/tessellate/StrokeHardwareTessellator.h"
|
||||
|
||||
#include "src/core/SkMathPriv.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/WangsFormula.h"
|
||||
#include "src/gpu/tessellate/shaders/GrTessellationShader.h"
|
||||
|
||||
#if SK_GPU_V1
|
||||
#include "src/gpu/GrOpFlushState.h"
|
||||
@ -657,7 +657,7 @@ private:
|
||||
SkPoint fLastControlPoint;
|
||||
|
||||
// Values for the current dynamic state (if any) that will get written out with each patch.
|
||||
GrStrokeTessellationShader::DynamicStroke fDynamicStroke;
|
||||
StrokeParams fDynamicStroke;
|
||||
GrVertexColor fDynamicColor;
|
||||
};
|
||||
|
||||
@ -696,34 +696,29 @@ SK_ALWAYS_INLINE bool cubic_has_cusp(const SkPoint p[4]) {
|
||||
} // namespace
|
||||
|
||||
|
||||
StrokeHardwareTessellator::StrokeHardwareTessellator(const GrShaderCaps& shaderCaps,
|
||||
PatchAttribs attribs,
|
||||
const SkMatrix& viewMatrix,
|
||||
PathStrokeList* pathStrokeList,
|
||||
std::array<float,2> matrixMinMaxScales)
|
||||
: StrokeTessellator(shaderCaps,
|
||||
GrStrokeTessellationShader::Mode::kHardwareTessellation,
|
||||
attribs,
|
||||
SkNextLog2(shaderCaps.maxTessellationSegments()),
|
||||
viewMatrix,
|
||||
pathStrokeList,
|
||||
matrixMinMaxScales) {
|
||||
}
|
||||
|
||||
void StrokeHardwareTessellator::prepare(GrMeshDrawTarget* target, int totalCombinedVerbCnt) {
|
||||
int StrokeHardwareTessellator::prepare(GrMeshDrawTarget* target,
|
||||
const SkMatrix& shaderMatrix,
|
||||
std::array<float,2> matrixMinMaxScales,
|
||||
PathStrokeList* pathStrokeList,
|
||||
int totalCombinedVerbCnt) {
|
||||
using JoinType = PatchWriter::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(fAttribs, target, fShader.viewMatrix(), fMatrixMinMaxScales[1],
|
||||
&fPatchChunks, fShader.vertexStride(), minPatchesPerChunk);
|
||||
PatchWriter patchWriter(fAttribs,
|
||||
target,
|
||||
shaderMatrix,
|
||||
matrixMinMaxScales[1],
|
||||
&fPatchChunks,
|
||||
sizeof(SkPoint) * 5 + PatchAttribsStride(fAttribs),
|
||||
minPatchesPerChunk);
|
||||
|
||||
if (!fShader.hasDynamicStroke()) {
|
||||
if (!(fAttribs & PatchAttribs::kStrokeParams)) {
|
||||
// Strokes are static. Calculate tolerances once.
|
||||
const SkStrokeRec& stroke = fPathStrokeList->fStroke;
|
||||
float localStrokeWidth = StrokeTolerances::GetLocalStrokeWidth(fMatrixMinMaxScales.data(),
|
||||
const SkStrokeRec& stroke = pathStrokeList->fStroke;
|
||||
float localStrokeWidth = StrokeTolerances::GetLocalStrokeWidth(matrixMinMaxScales.data(),
|
||||
stroke.getWidth());
|
||||
float numRadialSegmentsPerRadian = StrokeTolerances::CalcNumRadialSegmentsPerRadian(
|
||||
patchWriter.parametricPrecision(), localStrokeWidth);
|
||||
@ -734,15 +729,15 @@ void StrokeHardwareTessellator::prepare(GrMeshDrawTarget* target, int totalCombi
|
||||
// have dynamic strokes.
|
||||
StrokeToleranceBuffer toleranceBuffer(patchWriter.parametricPrecision());
|
||||
|
||||
for (PathStrokeList* pathStroke = fPathStrokeList; pathStroke; pathStroke = pathStroke->fNext) {
|
||||
for (PathStrokeList* pathStroke = pathStrokeList; pathStroke; pathStroke = pathStroke->fNext) {
|
||||
const SkStrokeRec& stroke = pathStroke->fStroke;
|
||||
if (fShader.hasDynamicStroke()) {
|
||||
if (fAttribs & PatchAttribs::kStrokeParams) {
|
||||
// Strokes are dynamic. Update tolerances with every new stroke.
|
||||
patchWriter.updateTolerances(toleranceBuffer.fetchRadialSegmentsPerRadian(pathStroke),
|
||||
stroke.getJoin());
|
||||
patchWriter.updateDynamicStroke(stroke);
|
||||
}
|
||||
if (fShader.hasDynamicColor()) {
|
||||
if (fAttribs & PatchAttribs::kColor) {
|
||||
patchWriter.updateDynamicColor(pathStroke->fColor);
|
||||
}
|
||||
|
||||
@ -758,13 +753,13 @@ void StrokeHardwareTessellator::prepare(GrMeshDrawTarget* target, int totalCombi
|
||||
// "A subpath ... consisting of a single moveto shall not be stroked."
|
||||
// https://www.w3.org/TR/SVG11/painting.html#StrokeProperties
|
||||
if (!contourIsEmpty) {
|
||||
patchWriter.writeCaps(p[-1], fShader.viewMatrix(), stroke);
|
||||
patchWriter.writeCaps(p[-1], shaderMatrix, stroke);
|
||||
}
|
||||
patchWriter.moveTo(p[0]);
|
||||
contourIsEmpty = true;
|
||||
continue;
|
||||
case SkPathVerb::kClose:
|
||||
patchWriter.writeClose(p[0], fShader.viewMatrix(), stroke);
|
||||
patchWriter.writeClose(p[0], shaderMatrix, stroke);
|
||||
contourIsEmpty = true;
|
||||
continue;
|
||||
case SkPathVerb::kLine:
|
||||
@ -886,9 +881,10 @@ void StrokeHardwareTessellator::prepare(GrMeshDrawTarget* target, int totalCombi
|
||||
}
|
||||
if (!contourIsEmpty) {
|
||||
const SkPoint* p = SkPathPriv::PointData(path);
|
||||
patchWriter.writeCaps(p[path.countPoints() - 1], fShader.viewMatrix(), stroke);
|
||||
patchWriter.writeCaps(p[path.countPoints() - 1], shaderMatrix, stroke);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if SK_GPU_V1
|
||||
|
@ -18,13 +18,13 @@ namespace skgpu {
|
||||
// MSAA if antialiasing is desired.
|
||||
class StrokeHardwareTessellator : public StrokeTessellator {
|
||||
public:
|
||||
StrokeHardwareTessellator(const GrShaderCaps& shaderCaps,
|
||||
PatchAttribs,
|
||||
const SkMatrix& viewMatrix,
|
||||
PathStrokeList* pathStrokeList,
|
||||
std::array<float,2> matrixMinMaxScales);
|
||||
StrokeHardwareTessellator(PatchAttribs attribs) : StrokeTessellator(attribs) {}
|
||||
|
||||
void prepare(GrMeshDrawTarget*, int totalCombinedVerbCnt) override;
|
||||
int prepare(GrMeshDrawTarget*,
|
||||
const SkMatrix& shaderMatrix,
|
||||
std::array<float,2> matrixMinMaxScales,
|
||||
PathStrokeList*,
|
||||
int totalCombinedVerbCnt) override;
|
||||
#if SK_GPU_V1
|
||||
void draw(GrOpFlushState*) const override;
|
||||
#endif
|
||||
|
@ -8,8 +8,10 @@
|
||||
#ifndef tessellate_StrokeTessellator_DEFINED
|
||||
#define tessellate_StrokeTessellator_DEFINED
|
||||
|
||||
#include "include/core/SkPath.h"
|
||||
#include "include/core/SkStrokeRec.h"
|
||||
#include "include/private/SkColorData.h"
|
||||
#include "src/gpu/tessellate/Tessellation.h"
|
||||
#include "src/gpu/tessellate/shaders/GrStrokeTessellationShader.h"
|
||||
|
||||
class GrMeshDrawTarget;
|
||||
class GrOpFlushState;
|
||||
@ -28,24 +30,17 @@ public:
|
||||
PathStrokeList* fNext = nullptr;
|
||||
};
|
||||
|
||||
StrokeTessellator(const GrShaderCaps& shaderCaps,
|
||||
GrStrokeTessellationShader::Mode shaderMode,
|
||||
PatchAttribs attribs,
|
||||
int8_t maxParametricSegments_log2,
|
||||
const SkMatrix& viewMatrix,
|
||||
PathStrokeList* pathStrokeList,
|
||||
std::array<float, 2> matrixMinMaxScales)
|
||||
: fAttribs(attribs)
|
||||
, fShader(shaderCaps, shaderMode, fAttribs, viewMatrix, pathStrokeList->fStroke,
|
||||
pathStrokeList->fColor, maxParametricSegments_log2)
|
||||
, fPathStrokeList(pathStrokeList)
|
||||
, fMatrixMinMaxScales(matrixMinMaxScales) {
|
||||
}
|
||||
|
||||
const GrTessellationShader* shader() const { return &fShader; }
|
||||
StrokeTessellator(PatchAttribs attribs) : fAttribs(attribs) {}
|
||||
|
||||
// Called before draw(). Prepares GPU buffers containing the geometry to tessellate.
|
||||
virtual void prepare(GrMeshDrawTarget*, int totalCombinedVerbCnt) = 0;
|
||||
//
|
||||
// Returns the fixed number of edges the tessellator will draw per patch, if using fixed-count
|
||||
// rendering, otherwise 0.
|
||||
virtual int prepare(GrMeshDrawTarget*,
|
||||
const SkMatrix& shaderMatrix,
|
||||
std::array<float,2> matrixMinMaxScales,
|
||||
PathStrokeList*,
|
||||
int totalCombinedVerbCnt) = 0;
|
||||
|
||||
#if SK_GPU_V1
|
||||
// Issues draw calls for the tessellated stroke. The caller is responsible for creating and
|
||||
@ -57,9 +52,6 @@ public:
|
||||
|
||||
protected:
|
||||
PatchAttribs fAttribs;
|
||||
GrStrokeTessellationShader fShader;
|
||||
PathStrokeList* fPathStrokeList;
|
||||
const std::array<float,2> fMatrixMinMaxScales;
|
||||
};
|
||||
|
||||
// These tolerances decide the number of parametric and radial segments the tessellator will
|
||||
|
@ -8,7 +8,7 @@
|
||||
#ifndef tessellate_Tessellation_DEFINED
|
||||
#define tessellate_Tessellation_DEFINED
|
||||
|
||||
#include "include/core/SkTypes.h"
|
||||
#include "include/core/SkStrokeRec.h"
|
||||
#include "include/gpu/GrTypes.h"
|
||||
#include "include/private/SkVx.h"
|
||||
|
||||
@ -20,35 +20,6 @@ namespace skgpu {
|
||||
|
||||
struct VertexWriter;
|
||||
|
||||
// Don't allow linearized segments to be off by more than 1/4th of a pixel from the true curve.
|
||||
SK_MAYBE_UNUSED constexpr static float kTessellationPrecision = 4;
|
||||
|
||||
// Optional attribs that are included in tessellation patches, following the control points and in
|
||||
// the same order as they appear here.
|
||||
enum class PatchAttribs {
|
||||
// Attribs.
|
||||
kNone = 0,
|
||||
kFanPoint = 1 << 0, // [float2] Used by wedges. This is the center point the wedges fan around.
|
||||
kStrokeParams = 1 << 1, // [float2] Used when strokes have different widths or join types.
|
||||
kColor = 1 << 2, // [ubyte4 or float4] Used when patches have different colors.
|
||||
kExplicitCurveType = 1 << 3, // [float] Used when GPU can't infer curve type based on infinity.
|
||||
|
||||
// Extra flags.
|
||||
kWideColorIfEnabled = 1 << 4, // If kColor is set, specifies it to be float4 wide color.
|
||||
};
|
||||
|
||||
GR_MAKE_BITFIELD_CLASS_OPS(PatchAttribs)
|
||||
|
||||
// Returns the packed size in bytes of a tessellation patch (or instance) in GPU buffers.
|
||||
constexpr size_t PatchStride(PatchAttribs attribs) {
|
||||
return sizeof(float) * 8 + // 4 control points
|
||||
(attribs & PatchAttribs::kFanPoint ? sizeof(float) * 2 : 0) +
|
||||
(attribs & PatchAttribs::kColor
|
||||
? (attribs & PatchAttribs::kWideColorIfEnabled ? sizeof(float)
|
||||
: sizeof(uint8_t)) * 4 : 0) +
|
||||
(attribs & PatchAttribs::kExplicitCurveType ? sizeof(float) : 0);
|
||||
}
|
||||
|
||||
// Use familiar type names from SkSL.
|
||||
template<int N> using vec = skvx::Vec<N, float>;
|
||||
using float2 = vec<2>;
|
||||
@ -93,6 +64,65 @@ AI constexpr float pow4(float x) { return pow2(x*x); }
|
||||
|
||||
#undef AI
|
||||
|
||||
// Don't allow linearized segments to be off by more than 1/4th of a pixel from the true curve.
|
||||
SK_MAYBE_UNUSED constexpr static float kTessellationPrecision = 4;
|
||||
|
||||
// Optional attribs that are included in tessellation patches, following the control points and in
|
||||
// the same order as they appear here.
|
||||
enum class PatchAttribs {
|
||||
// Attribs.
|
||||
kNone = 0,
|
||||
kFanPoint = 1 << 0, // [float2] Used by wedges. This is the center point the wedges fan around.
|
||||
kStrokeParams = 1 << 1, // [float2] Used when strokes have different widths or join types.
|
||||
kColor = 1 << 2, // [ubyte4 or float4] Used when patches have different colors.
|
||||
kExplicitCurveType = 1 << 3, // [float] Used when GPU can't infer curve type based on infinity.
|
||||
|
||||
// Extra flags.
|
||||
kWideColorIfEnabled = 1 << 4, // If kColor is set, specifies it to be float4 wide color.
|
||||
};
|
||||
|
||||
GR_MAKE_BITFIELD_CLASS_OPS(PatchAttribs)
|
||||
|
||||
// We encode all of a join's information in a single float value:
|
||||
//
|
||||
// Negative => Round Join
|
||||
// Zero => Bevel Join
|
||||
// Positive => Miter join, and the value is also the miter limit
|
||||
//
|
||||
static float GetJoinType(const SkStrokeRec& stroke) {
|
||||
switch (stroke.getJoin()) {
|
||||
case SkPaint::kRound_Join: return -1;
|
||||
case SkPaint::kBevel_Join: return 0;
|
||||
case SkPaint::kMiter_Join: SkASSERT(stroke.getMiter() >= 0); return stroke.getMiter();
|
||||
}
|
||||
SkUNREACHABLE;
|
||||
}
|
||||
|
||||
// This float2 gets written out with each patch/instance if PatchAttribs::kStrokeParams is enabled.
|
||||
struct StrokeParams {
|
||||
static bool StrokesHaveEqualParams(const SkStrokeRec& a, const SkStrokeRec& b) {
|
||||
return a.getWidth() == b.getWidth() && a.getJoin() == b.getJoin() &&
|
||||
(a.getJoin() != SkPaint::kMiter_Join || a.getMiter() == b.getMiter());
|
||||
}
|
||||
void set(const SkStrokeRec& stroke) {
|
||||
fRadius = stroke.getWidth() * .5f;
|
||||
fJoinType = GetJoinType(stroke);
|
||||
}
|
||||
float fRadius;
|
||||
float fJoinType; // See GetJoinType().
|
||||
};
|
||||
|
||||
// Returns the packed size in bytes of the attribs portion of tessellation patches (or instances) in
|
||||
// GPU buffers.
|
||||
constexpr size_t PatchAttribsStride(PatchAttribs attribs) {
|
||||
return (attribs & PatchAttribs::kFanPoint ? sizeof(float) * 2 : 0) +
|
||||
(attribs & PatchAttribs::kStrokeParams ? sizeof(float) * 2 : 0) +
|
||||
(attribs & PatchAttribs::kColor
|
||||
? (attribs & PatchAttribs::kWideColorIfEnabled ? sizeof(float)
|
||||
: sizeof(uint8_t)) * 4 : 0) +
|
||||
(attribs & PatchAttribs::kExplicitCurveType ? sizeof(float) : 0);
|
||||
}
|
||||
|
||||
// Don't tessellate paths that might have an individual curve that requires more than 1024 segments.
|
||||
// (See wangs_formula::worst_case_cubic). If this is the case, call "PreChopPathCurves" first.
|
||||
constexpr static float kMaxTessellationSegmentsPerCurve SK_MAYBE_UNUSED = 1024;
|
||||
|
@ -40,7 +40,8 @@ public:
|
||||
constexpr static Attribute kInputPointAttrib{"inputPoint", kFloat2_GrVertexAttribType,
|
||||
kFloat2_GrSLType};
|
||||
this->setVertexAttributes(&kInputPointAttrib, 1);
|
||||
SkASSERT(this->vertexStride() * 5 == skgpu::PatchStride(fAttribs));
|
||||
SkASSERT(this->vertexStride() * 5 ==
|
||||
sizeof(SkPoint) * 4 + skgpu::PatchAttribsStride(fAttribs));
|
||||
}
|
||||
|
||||
int maxTessellationSegments(const GrShaderCaps& shaderCaps) const override {
|
||||
@ -180,7 +181,8 @@ public:
|
||||
constexpr static Attribute kInputPointAttrib{"inputPoint", kFloat2_GrVertexAttribType,
|
||||
kFloat2_GrSLType};
|
||||
this->setVertexAttributes(&kInputPointAttrib, 1);
|
||||
SkASSERT(this->vertexStride() * 4 == skgpu::PatchStride(fAttribs));
|
||||
SkASSERT(this->vertexStride() * 4 ==
|
||||
sizeof(SkPoint) * 4 + skgpu::PatchAttribsStride(fAttribs));
|
||||
}
|
||||
|
||||
int maxTessellationSegments(const GrShaderCaps& shaderCaps) const override {
|
||||
|
@ -58,7 +58,8 @@ public:
|
||||
}
|
||||
this->setInstanceAttributes(fInstanceAttribs.data(), fInstanceAttribs.count());
|
||||
SkASSERT(fInstanceAttribs.count() <= kMaxInstanceAttribCount);
|
||||
SkASSERT(this->instanceStride() == skgpu::PatchStride(fAttribs));
|
||||
SkASSERT(this->instanceStride() ==
|
||||
sizeof(SkPoint) * 4 + skgpu::PatchAttribsStride(fAttribs));
|
||||
|
||||
constexpr static Attribute kVertexAttrib("resolveLevel_and_idx", kFloat2_GrVertexAttribType,
|
||||
kFloat2_GrSLType);
|
||||
|
@ -92,8 +92,10 @@ GrStrokeTessellationShader::GrStrokeTessellationShader(const GrShaderCaps& shade
|
||||
}
|
||||
if (fMode == Mode::kHardwareTessellation) {
|
||||
this->setVertexAttributes(fAttribs.data(), fAttribs.count());
|
||||
SkASSERT(this->vertexStride() == sizeof(SkPoint) * 5 + PatchAttribsStride(fPatchAttribs));
|
||||
} else {
|
||||
this->setInstanceAttributes(fAttribs.data(), fAttribs.count());
|
||||
SkASSERT(this->instanceStride() == sizeof(SkPoint) * 5 + PatchAttribsStride(fPatchAttribs));
|
||||
if (!shaderCaps.vertexIDSupport()) {
|
||||
constexpr static Attribute kVertexAttrib("edgeID", kFloat_GrVertexAttribType,
|
||||
kFloat_GrSLType);
|
||||
@ -371,7 +373,7 @@ void GrStrokeTessellationShader::Impl::setData(const GrGLSLProgramDataManager& p
|
||||
pdman.set4f(fTessControlArgsUniform,
|
||||
tolerances.fParametricPrecision, // PARAMETRIC_PRECISION
|
||||
tolerances.fNumRadialSegmentsPerRadian, // NUM_RADIAL_SEGMENTS_PER_RADIAN
|
||||
GrStrokeTessellationShader::GetJoinType(stroke), // JOIN_TYPE
|
||||
skgpu::GetJoinType(stroke), // JOIN_TYPE
|
||||
strokeRadius); // STROKE_RADIUS
|
||||
} else {
|
||||
SkASSERT(!stroke.isHairlineStyle());
|
||||
|
@ -33,62 +33,6 @@ public:
|
||||
kFixedCount
|
||||
};
|
||||
|
||||
// Returns the fixed number of edges that are always emitted with the given join type. If the
|
||||
// join is round, the caller needs to account for the additional radial edges on their own.
|
||||
// Specifically, each join always emits:
|
||||
//
|
||||
// * Two colocated edges at the beginning (a full-width edge to seam with the preceding stroke
|
||||
// and a half-width edge to begin the join).
|
||||
//
|
||||
// * An extra edge in the middle for miter joins, or else a variable number of radial edges
|
||||
// for round joins (the caller is responsible for counting radial edges from round joins).
|
||||
//
|
||||
// * A half-width edge at the end of the join that will be colocated with the first
|
||||
// (full-width) edge of the stroke.
|
||||
//
|
||||
constexpr static int NumFixedEdgesInJoin(SkPaint::Join joinType) {
|
||||
switch (joinType) {
|
||||
case SkPaint::kMiter_Join:
|
||||
return 4;
|
||||
case SkPaint::kRound_Join:
|
||||
// The caller is responsible for counting the variable number of middle, radial
|
||||
// segments on round joins.
|
||||
[[fallthrough]];
|
||||
case SkPaint::kBevel_Join:
|
||||
return 3;
|
||||
}
|
||||
SkUNREACHABLE;
|
||||
}
|
||||
|
||||
// We encode all of a join's information in a single float value:
|
||||
//
|
||||
// Negative => Round Join
|
||||
// Zero => Bevel Join
|
||||
// Positive => Miter join, and the value is also the miter limit
|
||||
//
|
||||
static float GetJoinType(const SkStrokeRec& stroke) {
|
||||
switch (stroke.getJoin()) {
|
||||
case SkPaint::kRound_Join: return -1;
|
||||
case SkPaint::kBevel_Join: return 0;
|
||||
case SkPaint::kMiter_Join: SkASSERT(stroke.getMiter() >= 0); return stroke.getMiter();
|
||||
}
|
||||
SkUNREACHABLE;
|
||||
}
|
||||
|
||||
// This struct gets written out to each patch or instance if kDynamicStroke is enabled.
|
||||
struct DynamicStroke {
|
||||
static bool StrokesHaveEqualDynamicState(const SkStrokeRec& a, const SkStrokeRec& b) {
|
||||
return a.getWidth() == b.getWidth() && a.getJoin() == b.getJoin() &&
|
||||
(a.getJoin() != SkPaint::kMiter_Join || a.getMiter() == b.getMiter());
|
||||
}
|
||||
void set(const SkStrokeRec& stroke) {
|
||||
fRadius = stroke.getWidth() * .5f;
|
||||
fJoinType = GetJoinType(stroke);
|
||||
}
|
||||
float fRadius;
|
||||
float fJoinType; // See GetJoinType().
|
||||
};
|
||||
|
||||
// 'viewMatrix' is applied to the geometry post tessellation. It cannot have perspective.
|
||||
GrStrokeTessellationShader(const GrShaderCaps&, Mode, PatchAttribs, const SkMatrix& viewMatrix,
|
||||
const SkStrokeRec&, SkPMColor4f, int8_t maxParametricSegments_log2);
|
||||
@ -109,13 +53,6 @@ public:
|
||||
fFixedCountNumTotalEdges = value;
|
||||
}
|
||||
|
||||
// Initializes the fallback vertex buffer that should be bound when drawing in Mode::kFixedCount
|
||||
// and sk_VertexID is not supported. Each vertex is a single float and each edge is composed of
|
||||
// two vertices, so the desired edge count in the buffer is presumed to be
|
||||
// "bufferSize / (sizeof(float) * 2)". The caller cannot draw more vertices than edgeCount * 2.
|
||||
static void InitializeVertexIDFallbackBuffer(skgpu::VertexWriter vertexWriter,
|
||||
size_t bufferSize);
|
||||
|
||||
private:
|
||||
const char* name() const override {
|
||||
switch (fMode) {
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
|
||||
#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
|
||||
#include "src/gpu/tessellate/StrokeFixedCountTessellator.h"
|
||||
#include "src/gpu/tessellate/WangsFormula.h"
|
||||
|
||||
using skgpu::VertexWriter;
|
||||
@ -180,7 +181,8 @@ void GrStrokeTessellationShader::InstancedImpl::onEmitCode(EmitArgs& args, GrGPA
|
||||
}
|
||||
} else {
|
||||
args.fVertBuilder->codeAppendf(R"(
|
||||
float numEdgesInJoin = %i;)", GrStrokeTessellationShader::NumFixedEdgesInJoin(joinType));
|
||||
float numEdgesInJoin = %i;)",
|
||||
skgpu::StrokeFixedCountTessellator::NumFixedEdgesInJoin(joinType));
|
||||
}
|
||||
|
||||
args.fVertBuilder->codeAppend(R"(
|
||||
@ -265,12 +267,3 @@ void GrStrokeTessellationShader::InstancedImpl::onEmitCode(EmitArgs& args, GrGPA
|
||||
|
||||
this->emitFragmentCode(shader, args);
|
||||
}
|
||||
|
||||
void GrStrokeTessellationShader::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;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user