Add an sk_VertexID workaround for tessellated stroking
Tessellation should now be completely free of its dependence on sk_VertexID. Bug: chromium:1220246 Change-Id: I4027099392b92e45aee7d8417945335352e3416e Reviewed-on: https://skia-review.googlesource.com/c/skia/+/423496 Commit-Queue: Chris Dalton <csmartdalton@google.com> Reviewed-by: Brian Salomon <bsalomon@google.com>
This commit is contained in:
parent
024668cf7f
commit
bb995e60a3
@ -222,11 +222,12 @@ static std::unique_ptr<GrStrokeTessellator> make_hw_tessellator(
|
||||
}
|
||||
|
||||
static std::unique_ptr<GrStrokeTessellator> make_fixed_count_tessellator(
|
||||
ShaderFlags shaderFlags, const GrShaderCaps&, const SkMatrix& viewMatrix,
|
||||
ShaderFlags shaderFlags, const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix,
|
||||
PathStrokeList* pathStrokeList, std::array<float, 2> matrixMinMaxScales,
|
||||
const SkRect& strokeCullBounds) {
|
||||
return std::make_unique<GrStrokeFixedCountTessellator>(shaderFlags, viewMatrix, pathStrokeList,
|
||||
matrixMinMaxScales, strokeCullBounds);
|
||||
matrixMinMaxScales, strokeCullBounds,
|
||||
shaderCaps);
|
||||
}
|
||||
|
||||
using MakePathStrokesFn = std::vector<PathStrokeList>(*)();
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "src/gpu/tessellate/GrStrokeFixedCountTessellator.h"
|
||||
|
||||
#include "src/core/SkGeometry.h"
|
||||
#include "src/gpu/GrResourceProvider.h"
|
||||
#include "src/gpu/geometry/GrPathUtils.h"
|
||||
#include "src/gpu/geometry/GrWangsFormula.h"
|
||||
#include "src/gpu/tessellate/GrCullTest.h"
|
||||
@ -238,12 +239,15 @@ GrStrokeFixedCountTessellator::GrStrokeFixedCountTessellator(ShaderFlags shaderF
|
||||
const SkMatrix& viewMatrix,
|
||||
PathStrokeList* pathStrokeList,
|
||||
std::array<float,2> matrixMinMaxScales,
|
||||
const SkRect& strokeCullBounds)
|
||||
const SkRect& strokeCullBounds,
|
||||
const GrShaderCaps& shaderCaps)
|
||||
: GrStrokeTessellator(GrStrokeTessellationShader::Mode::kFixedCount, shaderFlags,
|
||||
kMaxParametricSegments_log2, viewMatrix, pathStrokeList,
|
||||
matrixMinMaxScales, strokeCullBounds) {
|
||||
matrixMinMaxScales, strokeCullBounds, shaderCaps) {
|
||||
}
|
||||
|
||||
GR_DECLARE_STATIC_UNIQUE_KEY(gVertexIDFallbackBufferKey);
|
||||
|
||||
void GrStrokeFixedCountTessellator::prepare(GrMeshDrawTarget* target,
|
||||
int totalCombinedVerbCnt) {
|
||||
int maxEdgesInJoin = 0;
|
||||
@ -404,6 +408,26 @@ void GrStrokeFixedCountTessellator::prepare(GrMeshDrawTarget* target,
|
||||
// emit both because the join's edge is half-width and the stroke's is full-width.
|
||||
int fixedEdgeCount = 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);
|
||||
|
||||
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);
|
||||
|
||||
GR_DEFINE_STATIC_UNIQUE_KEY(gVertexIDFallbackBufferKey);
|
||||
|
||||
fVertexBufferIfNoIDSupport = target->resourceProvider()->findOrMakeStaticBuffer(
|
||||
GrGpuBufferType::kVertex,
|
||||
kMaxVerticesInFallbackBuffer * sizeof(float),
|
||||
gVertexIDFallbackBufferKey,
|
||||
GrStrokeTessellationShader::InitializeVertexIDFallbackBuffer);
|
||||
}
|
||||
|
||||
fShader.setFixedCountNumTotalEdges(fixedEdgeCount);
|
||||
fFixedVertexCount = fixedEdgeCount * 2;
|
||||
}
|
||||
@ -413,7 +437,7 @@ void GrStrokeFixedCountTessellator::draw(GrOpFlushState* flushState) const {
|
||||
return;
|
||||
}
|
||||
for (const auto& instanceChunk : fInstanceChunks) {
|
||||
flushState->bindBuffers(nullptr, instanceChunk.fBuffer, nullptr);
|
||||
flushState->bindBuffers(nullptr, instanceChunk.fBuffer, fVertexBufferIfNoIDSupport);
|
||||
flushState->drawInstanced(instanceChunk.fCount, instanceChunk.fBase, fFixedVertexCount, 0);
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ class GrStrokeFixedCountTessellator : public GrStrokeTessellator {
|
||||
public:
|
||||
GrStrokeFixedCountTessellator(ShaderFlags, const SkMatrix&, PathStrokeList*,
|
||||
std::array<float,2> matrixMinMaxScales,
|
||||
const SkRect& strokeCullBounds);
|
||||
const SkRect& strokeCullBounds, const GrShaderCaps&);
|
||||
|
||||
void prepare(GrMeshDrawTarget*, int totalCombinedVerbCnt) override;
|
||||
void draw(GrOpFlushState*) const override;
|
||||
@ -25,6 +25,9 @@ public:
|
||||
private:
|
||||
GrVertexChunkArray fInstanceChunks;
|
||||
int fFixedVertexCount = 0;
|
||||
|
||||
// Only used if sk_VertexID is not supported.
|
||||
sk_sp<const GrGpuBuffer> fVertexBufferIfNoIDSupport;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -709,10 +709,9 @@ GrStrokeHardwareTessellator::GrStrokeHardwareTessellator(ShaderFlags shaderFlags
|
||||
PathStrokeList* pathStrokeList,
|
||||
std::array<float,2> matrixMinMaxScales,
|
||||
const SkRect& strokeCullBounds)
|
||||
: GrStrokeTessellator(GrStrokeTessellationShader::Mode::kHardwareTessellation,
|
||||
shaderFlags, SkNextLog2(shaderCaps.maxTessellationSegments()),
|
||||
viewMatrix, pathStrokeList, matrixMinMaxScales,
|
||||
strokeCullBounds) {
|
||||
: GrStrokeTessellator(GrStrokeTessellationShader::Mode::kHardwareTessellation, shaderFlags,
|
||||
SkNextLog2(shaderCaps.maxTessellationSegments()), viewMatrix,
|
||||
pathStrokeList, matrixMinMaxScales, strokeCullBounds, shaderCaps) {
|
||||
}
|
||||
|
||||
void GrStrokeHardwareTessellator::prepare(GrMeshDrawTarget* target, int totalCombinedVerbCnt) {
|
||||
|
@ -198,7 +198,8 @@ void GrStrokeTessellateOp::prePrepareTessellator(GrTessellationShader::ProgramAr
|
||||
fTessellator = arena->make<GrStrokeFixedCountTessellator>(fShaderFlags, fViewMatrix,
|
||||
&fPathStrokeList,
|
||||
matrixMinMaxScales,
|
||||
strokeCullBounds);
|
||||
strokeCullBounds,
|
||||
*caps.shaderCaps());
|
||||
}
|
||||
|
||||
auto fillStencil = &GrUserStencilSettings::kUnused;
|
||||
|
@ -28,9 +28,9 @@ public:
|
||||
GrStrokeTessellator(GrStrokeTessellationShader::Mode shaderMode, ShaderFlags shaderFlags,
|
||||
int8_t maxParametricSegments_log2, const SkMatrix& viewMatrix,
|
||||
PathStrokeList* pathStrokeList, std::array<float,2> matrixMinMaxScales,
|
||||
const SkRect& strokeCullBounds)
|
||||
const SkRect& strokeCullBounds, const GrShaderCaps& shaderCaps)
|
||||
: fShader(shaderMode, shaderFlags, viewMatrix, pathStrokeList->fStroke,
|
||||
pathStrokeList->fColor, maxParametricSegments_log2)
|
||||
pathStrokeList->fColor, maxParametricSegments_log2, shaderCaps)
|
||||
, fPathStrokeList(pathStrokeList)
|
||||
, fMatrixMinMaxScales(matrixMinMaxScales)
|
||||
, fStrokeCullBounds(strokeCullBounds) {
|
||||
|
@ -78,8 +78,7 @@ GrPathRenderer::CanDrawPath GrTessellationPathRenderer::onCanDrawPath(
|
||||
return CanDrawPath::kNo;
|
||||
}
|
||||
if (!shape.style().isSimpleFill()) {
|
||||
if (shape.inverseFilled() ||
|
||||
!args.fCaps->shaderCaps()->vertexIDSupport()) {
|
||||
if (shape.inverseFilled()) {
|
||||
return CanDrawPath::kNo;
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,83 @@
|
||||
#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
|
||||
#include "src/gpu/tessellate/GrStrokeTessellator.h"
|
||||
|
||||
GrStrokeTessellationShader::GrStrokeTessellationShader(Mode mode, ShaderFlags shaderFlags,
|
||||
const SkMatrix& viewMatrix,
|
||||
const SkStrokeRec& stroke, SkPMColor4f color,
|
||||
int8_t maxParametricSegments_log2,
|
||||
const GrShaderCaps& shaderCaps)
|
||||
: GrTessellationShader(kTessellate_GrStrokeTessellationShader_ClassID,
|
||||
(mode == Mode::kHardwareTessellation)
|
||||
? GrPrimitiveType::kPatches
|
||||
: GrPrimitiveType::kTriangleStrip,
|
||||
(mode == Mode::kHardwareTessellation) ? 1 : 0, viewMatrix, color)
|
||||
, fMode(mode)
|
||||
, fShaderFlags(shaderFlags)
|
||||
, fStroke(stroke)
|
||||
, fMaxParametricSegments_log2(maxParametricSegments_log2) {
|
||||
if (fMode == Mode::kHardwareTessellation) {
|
||||
// A join calculates its starting angle using prevCtrlPtAttr.
|
||||
fAttribs.emplace_back("prevCtrlPtAttr", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
|
||||
// pts 0..3 define the stroke as a cubic bezier. If p3.y is infinity, then it's a conic
|
||||
// with w=p3.x.
|
||||
//
|
||||
// If p0 == prevCtrlPtAttr, then no join is emitted.
|
||||
//
|
||||
// pts=[p0, p3, p3, p3] is a reserved pattern that means this patch is a join only,
|
||||
// whose start and end tangents are (p0 - inputPrevCtrlPt) and (p3 - p0).
|
||||
//
|
||||
// pts=[p0, p0, p0, p3] is a reserved pattern that means this patch is a "bowtie", or
|
||||
// double-sided round join, anchored on p0 and rotating from (p0 - prevCtrlPtAttr) to
|
||||
// (p3 - p0).
|
||||
fAttribs.emplace_back("pts01Attr", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
|
||||
fAttribs.emplace_back("pts23Attr", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
|
||||
} else {
|
||||
// pts 0..3 define the stroke as a cubic bezier. If p3.y is infinity, then it's a conic
|
||||
// with w=p3.x.
|
||||
//
|
||||
// An empty stroke (p0==p1==p2==p3) is a special case that denotes a circle, or
|
||||
// 180-degree point stroke.
|
||||
fAttribs.emplace_back("pts01Attr", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
|
||||
fAttribs.emplace_back("pts23Attr", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
|
||||
if (fMode == Mode::kLog2Indirect) {
|
||||
// argsAttr.xy contains the lastControlPoint for setting up the join.
|
||||
//
|
||||
// "argsAttr.z=numTotalEdges" tells the shader the literal number of edges in the
|
||||
// triangle strip being rendered (i.e., it should be vertexCount/2). If
|
||||
// numTotalEdges is negative and the join type is "kRound", it also instructs the
|
||||
// shader to only allocate one segment the preceding round join.
|
||||
fAttribs.emplace_back("argsAttr", kFloat3_GrVertexAttribType, kFloat3_GrSLType);
|
||||
} else {
|
||||
SkASSERT(fMode == Mode::kFixedCount);
|
||||
// argsAttr contains the lastControlPoint for setting up the join.
|
||||
fAttribs.emplace_back("argsAttr", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
|
||||
}
|
||||
}
|
||||
if (fShaderFlags & ShaderFlags::kDynamicStroke) {
|
||||
fAttribs.emplace_back("dynamicStrokeAttr", kFloat2_GrVertexAttribType,
|
||||
kFloat2_GrSLType);
|
||||
}
|
||||
if (fShaderFlags & ShaderFlags::kDynamicColor) {
|
||||
fAttribs.emplace_back("dynamicColorAttr",
|
||||
(fShaderFlags & ShaderFlags::kWideColor)
|
||||
? kFloat4_GrVertexAttribType
|
||||
: kUByte4_norm_GrVertexAttribType,
|
||||
kHalf4_GrSLType);
|
||||
}
|
||||
if (fMode == Mode::kHardwareTessellation) {
|
||||
this->setVertexAttributes(fAttribs.data(), fAttribs.count());
|
||||
} else {
|
||||
this->setInstanceAttributes(fAttribs.data(), fAttribs.count());
|
||||
if (!shaderCaps.vertexIDSupport()) {
|
||||
constexpr static Attribute kVertexAttrib("edgeID", kFloat_GrVertexAttribType,
|
||||
kFloat_GrSLType);
|
||||
this->setVertexAttributes(&kVertexAttrib, 1);
|
||||
}
|
||||
}
|
||||
SkASSERT(fAttribs.count() <= kMaxAttribCount);
|
||||
}
|
||||
|
||||
|
||||
// The built-in atan() is undefined when x==0. This method relieves that restriction, but also can
|
||||
// return values larger than 2*PI. This shouldn't matter for our purposes.
|
||||
const char* GrStrokeTessellationShader::Impl::kAtan2Fn = R"(
|
||||
|
@ -98,74 +98,8 @@ public:
|
||||
};
|
||||
|
||||
// 'viewMatrix' is applied to the geometry post tessellation. It cannot have perspective.
|
||||
GrStrokeTessellationShader(Mode mode, ShaderFlags shaderFlags, const SkMatrix& viewMatrix,
|
||||
const SkStrokeRec& stroke, SkPMColor4f color,
|
||||
int8_t maxParametricSegments_log2)
|
||||
: GrTessellationShader(kTessellate_GrStrokeTessellationShader_ClassID,
|
||||
(mode == Mode::kHardwareTessellation)
|
||||
? GrPrimitiveType::kPatches
|
||||
: GrPrimitiveType::kTriangleStrip,
|
||||
(mode == Mode::kHardwareTessellation) ? 1 : 0, viewMatrix, color)
|
||||
, fMode(mode)
|
||||
, fShaderFlags(shaderFlags)
|
||||
, fStroke(stroke)
|
||||
, fMaxParametricSegments_log2(maxParametricSegments_log2) {
|
||||
if (fMode == Mode::kHardwareTessellation) {
|
||||
// A join calculates its starting angle using prevCtrlPtAttr.
|
||||
fAttribs.emplace_back("prevCtrlPtAttr", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
|
||||
// pts 0..3 define the stroke as a cubic bezier. If p3.y is infinity, then it's a conic
|
||||
// with w=p3.x.
|
||||
//
|
||||
// If p0 == prevCtrlPtAttr, then no join is emitted.
|
||||
//
|
||||
// pts=[p0, p3, p3, p3] is a reserved pattern that means this patch is a join only,
|
||||
// whose start and end tangents are (p0 - inputPrevCtrlPt) and (p3 - p0).
|
||||
//
|
||||
// pts=[p0, p0, p0, p3] is a reserved pattern that means this patch is a "bowtie", or
|
||||
// double-sided round join, anchored on p0 and rotating from (p0 - prevCtrlPtAttr) to
|
||||
// (p3 - p0).
|
||||
fAttribs.emplace_back("pts01Attr", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
|
||||
fAttribs.emplace_back("pts23Attr", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
|
||||
} else {
|
||||
// pts 0..3 define the stroke as a cubic bezier. If p3.y is infinity, then it's a conic
|
||||
// with w=p3.x.
|
||||
//
|
||||
// An empty stroke (p0==p1==p2==p3) is a special case that denotes a circle, or
|
||||
// 180-degree point stroke.
|
||||
fAttribs.emplace_back("pts01Attr", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
|
||||
fAttribs.emplace_back("pts23Attr", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
|
||||
if (fMode == Mode::kLog2Indirect) {
|
||||
// argsAttr.xy contains the lastControlPoint for setting up the join.
|
||||
//
|
||||
// "argsAttr.z=numTotalEdges" tells the shader the literal number of edges in the
|
||||
// triangle strip being rendered (i.e., it should be vertexCount/2). If
|
||||
// numTotalEdges is negative and the join type is "kRound", it also instructs the
|
||||
// shader to only allocate one segment the preceding round join.
|
||||
fAttribs.emplace_back("argsAttr", kFloat3_GrVertexAttribType, kFloat3_GrSLType);
|
||||
} else {
|
||||
SkASSERT(fMode == Mode::kFixedCount);
|
||||
// argsAttr contains the lastControlPoint for setting up the join.
|
||||
fAttribs.emplace_back("argsAttr", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
|
||||
}
|
||||
}
|
||||
if (fShaderFlags & ShaderFlags::kDynamicStroke) {
|
||||
fAttribs.emplace_back("dynamicStrokeAttr", kFloat2_GrVertexAttribType,
|
||||
kFloat2_GrSLType);
|
||||
}
|
||||
if (fShaderFlags & ShaderFlags::kDynamicColor) {
|
||||
fAttribs.emplace_back("dynamicColorAttr",
|
||||
(fShaderFlags & ShaderFlags::kWideColor)
|
||||
? kFloat4_GrVertexAttribType
|
||||
: kUByte4_norm_GrVertexAttribType,
|
||||
kHalf4_GrSLType);
|
||||
}
|
||||
if (fMode == Mode::kHardwareTessellation) {
|
||||
this->setVertexAttributes(fAttribs.data(), fAttribs.count());
|
||||
} else {
|
||||
this->setInstanceAttributes(fAttribs.data(), fAttribs.count());
|
||||
}
|
||||
SkASSERT(fAttribs.count() <= kMaxAttribCount);
|
||||
}
|
||||
GrStrokeTessellationShader(Mode, ShaderFlags, const SkMatrix& viewMatrix, const SkStrokeRec&,
|
||||
SkPMColor4f, int8_t maxParametricSegments_log2, const GrShaderCaps&);
|
||||
|
||||
Mode mode() const { return fMode; }
|
||||
ShaderFlags flags() const { return fShaderFlags; }
|
||||
@ -182,6 +116,12 @@ 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(GrVertexWriter vertexWriter, size_t bufferSize);
|
||||
|
||||
private:
|
||||
const char* name() const override {
|
||||
switch (fMode) {
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
|
||||
#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
|
||||
#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
|
||||
#include "src/gpu/tessellate/GrStrokeTessellator.h"
|
||||
|
||||
void GrStrokeTessellationShader::InstancedImpl::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
|
||||
const auto& shader = args.fGeomProc.cast<GrStrokeTessellationShader>();
|
||||
@ -126,6 +125,15 @@ void GrStrokeTessellationShader::InstancedImpl::onEmitCode(EmitArgs& args, GrGPA
|
||||
tan1 = float2(-1,0);
|
||||
})");
|
||||
|
||||
if (args.fShaderCaps->vertexIDSupport()) {
|
||||
// If we don't have sk_VertexID support then "edgeID" already came in as a vertex attrib.
|
||||
args.fVertBuilder->codeAppend(R"(
|
||||
float edgeID = float(sk_VertexID >> 1);
|
||||
if ((sk_VertexID & 1) != 0) {
|
||||
edgeID = -edgeID;
|
||||
})");
|
||||
}
|
||||
|
||||
// Potential optimization: (shader.hasDynamicStroke() && shader.hasRoundJoins())?
|
||||
if (shader.stroke().getJoin() == SkPaint::kRound_Join || shader.hasDynamicStroke()) {
|
||||
args.fVertBuilder->codeAppend(R"(
|
||||
@ -167,7 +175,7 @@ void GrStrokeTessellationShader::InstancedImpl::onEmitCode(EmitArgs& args, GrGPA
|
||||
// NOTE: Since the curve is not allowed to inflect, we can just check F'(.5) x F''(.5).
|
||||
// NOTE: F'(.5) x F''(.5) has the same sign as (P2 - P0) x (P3 - P1)
|
||||
float turn = cross(P[2] - P[0], P[3] - P[1]);
|
||||
float combinedEdgeID = float(sk_VertexID >> 1) - numEdgesInJoin;
|
||||
float combinedEdgeID = abs(edgeID) - numEdgesInJoin;
|
||||
if (combinedEdgeID < 0) {
|
||||
tan1 = tan0;
|
||||
// Don't let tan0 become zero. The code as-is isn't built to handle that case. tan0=0
|
||||
@ -188,7 +196,7 @@ void GrStrokeTessellationShader::InstancedImpl::onEmitCode(EmitArgs& args, GrGPA
|
||||
}
|
||||
|
||||
float numRadialSegments;
|
||||
float strokeOutset = ((sk_VertexID & 1) == 0) ? +1 : -1;
|
||||
float strokeOutset = sign(edgeID);
|
||||
if (combinedEdgeID < 0) {
|
||||
// We belong to the preceding join. The first and final edges get duplicated, so we only
|
||||
// have "numEdgesInJoin - 2" segments.
|
||||
@ -235,8 +243,8 @@ void GrStrokeTessellationShader::InstancedImpl::onEmitCode(EmitArgs& args, GrGPA
|
||||
|
||||
if (joinType == SkPaint::kMiter_Join || shader.hasDynamicStroke()) {
|
||||
args.fVertBuilder->codeAppendf(R"(
|
||||
// Vertices #4 and #5 belong to the edge of the join that extends to the miter point.
|
||||
if ((sk_VertexID | 1) == (4 | 5) && %s) {
|
||||
// Edge #2 extends to the miter point.
|
||||
if (abs(edgeID) == 2 && %s) {
|
||||
strokeOutset *= miter_extent(cosTheta, JOIN_TYPE/*miterLimit*/);
|
||||
})", shader.hasDynamicStroke() ? "JOIN_TYPE > 0/*Is the join a miter type?*/" : "true");
|
||||
}
|
||||
@ -245,3 +253,13 @@ void GrStrokeTessellationShader::InstancedImpl::onEmitCode(EmitArgs& args, GrGPA
|
||||
|
||||
this->emitFragmentCode(shader, args);
|
||||
}
|
||||
|
||||
void GrStrokeTessellationShader::InitializeVertexIDFallbackBuffer(GrVertexWriter vertexWriter,
|
||||
size_t bufferSize) {
|
||||
SkASSERT(bufferSize % (sizeof(float) * 2) == 0);
|
||||
int edgeCount = bufferSize / (sizeof(float) * 2);
|
||||
for (int i = 0; i < edgeCount; ++i) {
|
||||
vertexWriter.write<float>(i);
|
||||
vertexWriter.write<float>(-i);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user