blob: 12c86b603efe54982a119c8ccddbb64e74e004b0 [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/shaders/GrPathTessellationShader.h"
#include "src/gpu/effects/GrDisableColorXP.h"
#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
#include "src/gpu/glsl/GrGLSLVarying.h"
#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
namespace {
// Draws a simple array of triangles.
class SimpleTriangleShader : public GrPathTessellationShader {
public:
SimpleTriangleShader(const SkMatrix& viewMatrix, SkPMColor4f color)
: GrPathTessellationShader(kTessellate_SimpleTriangleShader_ClassID,
GrPrimitiveType::kTriangles, 0, viewMatrix, color) {
constexpr static Attribute kInputPointAttrib{"inputPoint", kFloat2_GrVertexAttribType,
kFloat2_GrSLType};
this->setVertexAttributes(&kInputPointAttrib, 1);
}
private:
const char* name() const final { return "tessellate_SimpleTriangleShader"; }
void addToKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {}
std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const final;
};
std::unique_ptr<GrGeometryProcessor::ProgramImpl> SimpleTriangleShader::makeProgramImpl(
const GrShaderCaps&) const {
class Impl : public GrPathTessellationShader::Impl {
void emitVertexCode(const GrShaderCaps&, const GrPathTessellationShader&,
GrGLSLVertexBuilder* v, GrGPArgs* gpArgs) override {
v->codeAppend(R"(
float2 localcoord = inputPoint;
float2 vertexpos = AFFINE_MATRIX * localcoord + TRANSLATE;)");
gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertexpos");
}
};
return std::make_unique<Impl>();
}
} // namespace
GrPathTessellationShader* GrPathTessellationShader::MakeSimpleTriangleShader(
SkArenaAlloc* arena, const SkMatrix& viewMatrix, const SkPMColor4f& color) {
return arena->make<SimpleTriangleShader>(viewMatrix, color);
}
const GrPipeline* GrPathTessellationShader::MakeStencilOnlyPipeline(
const ProgramArgs& args,
GrAAType aaType,
skgpu::tess::TessellationPathFlags pathFlags,
const GrAppliedHardClip& hardClip) {
using PathFlags = skgpu::tess::TessellationPathFlags;
GrPipeline::InitArgs pipelineArgs;
if (args.fCaps->wireframeSupport() && (pathFlags & PathFlags::kWireframe)) {
pipelineArgs.fInputFlags |= GrPipeline::InputFlags::kWireframe;
}
pipelineArgs.fCaps = args.fCaps;
return args.fArena->make<GrPipeline>(pipelineArgs,
GrDisableColorXPFactory::MakeXferProcessor(),
hardClip);
}
// Evaluate our point of interest using numerically stable linear interpolations. We add our own
// "safe_mix" method to guarantee we get exactly "b" when T=1. The builtin mix() function seems
// spec'd to behave this way, but empirical results results have shown it does not always.
const char* GrPathTessellationShader::Impl::kEvalRationalCubicFn = R"(
float3 safe_mix(float3 a, float3 b, float T, float one_minus_T) {
return a*one_minus_T + b*T;
}
float2 eval_rational_cubic(float4x3 P, float T) {
float one_minus_T = 1.0 - T;
float3 ab = safe_mix(P[0], P[1], T, one_minus_T);
float3 bc = safe_mix(P[1], P[2], T, one_minus_T);
float3 cd = safe_mix(P[2], P[3], T, one_minus_T);
float3 abc = safe_mix(ab, bc, T, one_minus_T);
float3 bcd = safe_mix(bc, cd, T, one_minus_T);
float3 abcd = safe_mix(abc, bcd, T, one_minus_T);
return abcd.xy / abcd.z;
})";
void GrPathTessellationShader::Impl::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
const auto& shader = args.fGeomProc.cast<GrPathTessellationShader>();
args.fVaryingHandler->emitAttributes(shader);
// Vertex shader.
const char* affineMatrix, *translate;
fAffineMatrixUniform = args.fUniformHandler->addUniform(nullptr, kVertex_GrShaderFlag,
kFloat4_GrSLType, "affineMatrix",
&affineMatrix);
fTranslateUniform = args.fUniformHandler->addUniform(nullptr, kVertex_GrShaderFlag,
kFloat2_GrSLType, "translate", &translate);
args.fVertBuilder->codeAppendf("float2x2 AFFINE_MATRIX = float2x2(%s);", affineMatrix);
args.fVertBuilder->codeAppendf("float2 TRANSLATE = %s;", translate);
this->emitVertexCode(*args.fShaderCaps, shader, args.fVertBuilder, gpArgs);
// Fragment shader.
const char* color;
fColorUniform = args.fUniformHandler->addUniform(nullptr, kFragment_GrShaderFlag,
kHalf4_GrSLType, "color", &color);
args.fFragBuilder->codeAppendf("half4 %s = %s;", args.fOutputColor, color);
args.fFragBuilder->codeAppendf("const half4 %s = half4(1);", args.fOutputCoverage);
}
void GrPathTessellationShader::Impl::setData(const GrGLSLProgramDataManager& pdman, const
GrShaderCaps&, const GrGeometryProcessor& geomProc) {
const auto& shader = geomProc.cast<GrTessellationShader>();
const SkMatrix& m = shader.viewMatrix();
pdman.set4f(fAffineMatrixUniform, m.getScaleX(), m.getSkewY(), m.getSkewX(), m.getScaleY());
pdman.set2f(fTranslateUniform, m.getTranslateX(), m.getTranslateY());
const SkPMColor4f& color = shader.color();
pdman.set4f(fColorUniform, color.fR, color.fG, color.fB, color.fA);
}