blob: 9fc1ad131c3b5ad8ce5f09b1ea7e6fdc14b22f91 [file] [log] [blame]
/*
* Copyright 2019 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/tessellate/GrTessellateWedgeShader.h"
#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
#include "src/gpu/glsl/GrGLSLVarying.h"
#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
class GrTessellateWedgeShader::Impl : public GrGLSLGeometryProcessor {
void onEmitCode(EmitArgs&, GrGPArgs*) override;
void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
const CoordTransformRange& transformRange) override;
GrGLSLUniformHandler::UniformHandle fViewMatrixUniform;
};
void GrTessellateWedgeShader::Impl::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
const char* viewMatrix;
fViewMatrixUniform = args.fUniformHandler->addUniform(
kVertex_GrShaderFlag, kFloat3x3_GrSLType, "view_matrix", &viewMatrix);
args.fVaryingHandler->emitAttributes(args.fGP.cast<GrTessellateWedgeShader>());
args.fVertBuilder->declareGlobal(GrShaderVar(
"P_", kFloat2_GrSLType, GrShaderVar::kOut_TypeModifier));
args.fVertBuilder->codeAppendf("P_ = (%s * float3(P, 1)).xy;", viewMatrix);
// No fragment shader.
};
SkString GrTessellateWedgeShader::getTessControlShaderGLSL(const char* versionAndExtensionDecls,
const GrShaderCaps&) const {
SkString code(versionAndExtensionDecls);
code.append(R"(
layout(vertices = 1) out;
in vec2 P_[];
out mat4x2 P[];
out vec2 fanpoint[];
// Wang's formula for cubics (1985) gives us the number of evenly spaced (in the
// parametric sense) line segments that are guaranteed to be within a distance of
// "MAX_LINEARIZATION_ERROR" from the actual curve.
#define MAX_LINEARIZATION_ERROR 0.25 // 1/4 pixel
float wangs_formula_cubic(vec2 p0, vec2 p1, vec2 p2, vec2 p3) {
float k = (3.0 * 2.0) / (8.0 * MAX_LINEARIZATION_ERROR);
float f = sqrt(k * length(max(abs(p2 - p1*2.0 + p0),
abs(p3 - p2*2.0 + p1))));
return max(1.0, ceil(f));
}
void main() {
// Calculate how many triangles we need to linearize the curve.
float num_segments = wangs_formula_cubic(P_[0], P_[1], P_[2], P_[3]);
// Tessellate the first side of the patch into num_segments triangles.
gl_TessLevelOuter[0] = num_segments;
// Leave the other two sides of the patch as single segments.
gl_TessLevelOuter[1] = 1.0;
gl_TessLevelOuter[2] = 1.0;
// Changing the inner level to 1 when num_segments == 1 collapses the entire
// patch to a single triangle. Otherwise, we need an inner level of 2 so our curve
// triangles have an interior point to originate from.
gl_TessLevelInner[0] = min(num_segments, 2.0);
P[gl_InvocationID /*== 0*/] = mat4x2(P_[0], P_[1], P_[2], P_[3]);
fanpoint[gl_InvocationID /*== 0*/] = P_[4];
})");
return code;
}
SkString GrTessellateWedgeShader::getTessEvaluationShaderGLSL(const char* versionAndExtensionDecls,
const GrShaderCaps&) const {
SkString code(versionAndExtensionDecls);
code.append(R"(
layout(triangles, equal_spacing, cw) in;
uniform vec4 sk_RTAdjust;
in mat4x2 P[];
in vec2 fanpoint[];
void main() {
vec2 p0 = P[0][0], p1 = P[0][1], p2 = P[0][2], p3 = P[0][3];
// Locate our parametric point of interest. It is equal to the barycentric
// y-coordinate if we are a vertex on the tessellated edge of the triangle patch,
// 0.5 if we are the patch's interior vertex, or N/A if we are the fan point.
// NOTE: We are on the tessellated edge when the barycentric x-coordinate == 0.
float T = (gl_TessCoord.x == 0.0) ? gl_TessCoord.y : 0.5;
// Evaluate our point of interest using numerically stable mix() operations.
vec2 ab = mix(p0, p1, T);
vec2 bc = mix(p1, p2, T);
vec2 cd = mix(p2, p3, T);
vec2 abc = mix(ab, bc, T);
vec2 bcd = mix(bc, cd, T);
vec2 vertexpos = mix(abc, bcd, T);
if (gl_TessCoord.x == 1.0) {
// We are the anchor point that fans from the center of the curve's contour.
vertexpos = fanpoint[0];
} else if (gl_TessCoord.x != 0.0) {
// We are the interior point of the patch; center it inside [C(0), C(.5), C(1)].
vertexpos = (p0 + vertexpos + p3) / 3.0;
}
gl_Position = vec4(vertexpos * sk_RTAdjust.xz + sk_RTAdjust.yw, 0.0, 1.0);
})");
return code;
}
void GrTessellateWedgeShader::Impl::setData(const GrGLSLProgramDataManager& pdman,
const GrPrimitiveProcessor& primProc,
const CoordTransformRange& transformRange) {
const GrTessellateWedgeShader& shader = primProc.cast<GrTessellateWedgeShader>();
pdman.setSkMatrix(fViewMatrixUniform, shader.fViewMatrix);
}
GrGLSLPrimitiveProcessor* GrTessellateWedgeShader::createGLSLInstance(const GrShaderCaps&) const {
return new Impl;
}