blob: 852d7e4e924aef8943afaff0c78acb57e02db900 [file] [log] [blame]
Chris Dalton5a2f9622019-12-27 14:56:38 -07001/*
2 * Copyright 2019 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "gm/gm.h"
9
10#include "src/gpu/GrCaps.h"
11#include "src/gpu/GrContextPriv.h"
12#include "src/gpu/GrMemoryPool.h"
13#include "src/gpu/GrMesh.h"
14#include "src/gpu/GrOpFlushState.h"
15#include "src/gpu/GrOpsRenderPass.h"
16#include "src/gpu/GrPipeline.h"
17#include "src/gpu/GrPrimitiveProcessor.h"
18#include "src/gpu/GrProgramInfo.h"
19#include "src/gpu/GrRecordingContextPriv.h"
20#include "src/gpu/GrRenderTargetContext.h"
21#include "src/gpu/GrRenderTargetContextPriv.h"
22#include "src/gpu/GrShaderCaps.h"
23#include "src/gpu/GrShaderVar.h"
24#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
25#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
26#include "src/gpu/glsl/GrGLSLPrimitiveProcessor.h"
27#include "src/gpu/glsl/GrGLSLVarying.h"
28#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
29#include "src/gpu/ops/GrDrawOp.h"
30
31namespace skiagm {
32
33constexpr static GrGeometryProcessor::Attribute kPositionAttrib =
34 {"position", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
35
36constexpr static std::array<float, 3> kTri1[3] = {
37 {20.5f,20.5f,1}, {170.5f,280.5f,4}, {320.5f,20.5f,1}};
38constexpr static std::array<float, 3> kTri2[3] = {
39 {640.5f,280.5f,3}, {490.5f,20.5f,1}, {340.5f,280.5f,6}};
40constexpr static SkRect kRect = {20.5f, 340.5f, 640.5f, 480.5f};
41
42constexpr static int kWidth = (int)kRect.fRight + 21;
43constexpr static int kHeight = (int)kRect.fBottom + 21;
44
45/**
46 * This is a GPU-backend specific test. It ensures that tessellation works as expected by drawing
47 * several triangles. The test passes as long as the triangle tessellations match the reference
48 * images on gold.
49 */
50class TessellationGM : public GpuGM {
51 SkString onShortName() override { return SkString("tessellation"); }
52 SkISize onISize() override { return {kWidth, kHeight}; }
53 DrawResult onDraw(GrContext*, GrRenderTargetContext*, SkCanvas*, SkString*) override;
54};
55
56
57class TessellationTestTriShader : public GrGeometryProcessor {
58public:
59 TessellationTestTriShader(const SkMatrix& viewMatrix)
60 : GrGeometryProcessor(kTessellationTestTriShader_ClassID), fViewMatrix(viewMatrix) {
61 this->setVertexAttributes(&kPositionAttrib, 1);
62 this->setWillUseTessellationShaders();
63 }
64
65private:
66 const char* name() const final { return "TessellationTestTriShader"; }
67 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {}
68
69 class Impl : public GrGLSLGeometryProcessor {
70 void onEmitCode(EmitArgs& args, GrGPArgs*) override {
71 args.fVaryingHandler->emitAttributes(args.fGP.cast<TessellationTestTriShader>());
72 const char* viewMatrix;
73 fViewMatrixUniform = args.fUniformHandler->addUniform(
74 kVertex_GrShaderFlag, kFloat3x3_GrSLType, "view_matrix", &viewMatrix);
75 args.fVertBuilder->declareGlobal(
76 GrShaderVar("P_", kFloat3_GrSLType, GrShaderVar::kOut_TypeModifier));
77 args.fVertBuilder->codeAppendf(R"(
78 P_.xy = (%s * float3(position.xy, 1)).xy;
79 P_.z = position.z;)", viewMatrix);
80 // GrGLProgramBuilder will call writeTess*ShaderGLSL when it is compiling.
81 this->writeFragmentShader(args.fFragBuilder, args.fOutputColor, args.fOutputCoverage);
82 }
83 void writeFragmentShader(GrGLSLFPFragmentBuilder*, const char* color, const char* coverage);
84 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
85 const CoordTransformRange&) override {
86 pdman.setSkMatrix(fViewMatrixUniform,
87 proc.cast<TessellationTestTriShader>().fViewMatrix);
88 }
89 GrGLSLUniformHandler::UniformHandle fViewMatrixUniform;
90 };
91
92 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
93 return new Impl;
94 }
95
96 SkString getTessControlShaderGLSL(const char* versionAndExtensionDecls,
97 const GrShaderCaps&) const override;
98 SkString getTessEvaluationShaderGLSL(const char* versionAndExtensionDecls,
99 const GrShaderCaps&) const override;
100
101 const SkMatrix fViewMatrix;
102};
103
104SkString TessellationTestTriShader::getTessControlShaderGLSL(
105 const char* versionAndExtensionDecls, const GrShaderCaps&) const {
106 SkString code(versionAndExtensionDecls);
107 code.append(R"(
108 layout(vertices = 3) out;
109
110 in vec3 P_[];
111 out vec3 P[];
112
113 void main() {
114 P[gl_InvocationID] = P_[gl_InvocationID];
115 gl_TessLevelOuter[gl_InvocationID] = P_[gl_InvocationID].z;
116 gl_TessLevelInner[0] = 2.0;
117 })");
118
119 return code;
120}
121
122SkString TessellationTestTriShader::getTessEvaluationShaderGLSL(
123 const char* versionAndExtensionDecls, const GrShaderCaps&) const {
124 SkString code(versionAndExtensionDecls);
125 code.append(R"(
126 layout(triangles, equal_spacing, cw) in;
127
128 uniform vec4 sk_RTAdjust;
129
130 in vec3 P[];
131 out vec3 barycentric_coord;
132
133 void main() {
134 vec2 devcoord = mat3x2(P[0].xy, P[1].xy, P[2].xy) * gl_TessCoord.xyz;
135 devcoord = round(devcoord - .5) + .5; // Make horz and vert lines on px bounds.
136 gl_Position = vec4(devcoord.xy * sk_RTAdjust.xz + sk_RTAdjust.yw, 0.0, 1.0);
137
138 float i = 0.0;
139 if (gl_TessCoord.y == 0.0) {
140 i += gl_TessCoord.z * P[1].z;
141 } else {
142 i += P[1].z;
143 if (gl_TessCoord.x == 0.0) {
144 i += gl_TessCoord.y * P[0].z;
145 } else {
146 i += P[0].z;
Chris Dalton8dae7eb2019-12-27 22:20:55 -0700147 if (gl_TessCoord.z == 0.0) {
148 i += gl_TessCoord.x * P[2].z;
149 } else {
150 barycentric_coord = vec3(0, 1, 0);
151 return;
152 }
Chris Dalton5a2f9622019-12-27 14:56:38 -0700153 }
154 }
Chris Dalton8dae7eb2019-12-27 22:20:55 -0700155 i = abs(mod(i, 2.0) - 1.0);
156 barycentric_coord = vec3(i, 0, 1.0 - i);
157 })");
Chris Dalton5a2f9622019-12-27 14:56:38 -0700158
159 return code;
160}
161
162void TessellationTestTriShader::Impl::writeFragmentShader(
163 GrGLSLFPFragmentBuilder* f, const char* color, const char* coverage) {
164 f->declareGlobal(
165 GrShaderVar("barycentric_coord", kFloat3_GrSLType, GrShaderVar::kIn_TypeModifier));
166 f->codeAppendf(R"(
167 half3 d = half3(1 - barycentric_coord/fwidth(barycentric_coord));
168 half coverage = max(max(d.x, d.y), d.z);
169 %s = half4(0, coverage, coverage, 1);
170 %s = half4(1);)", color, coverage);
171}
172
173class TessellationTestRectShader : public GrGeometryProcessor {
174public:
175 TessellationTestRectShader(const SkMatrix& viewMatrix)
176 : GrGeometryProcessor(kTessellationTestTriShader_ClassID), fViewMatrix(viewMatrix) {
177 this->setWillUseTessellationShaders();
178 }
179
180private:
181 const char* name() const final { return "TessellationTestRectShader"; }
182 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {}
183
184 class Impl : public GrGLSLGeometryProcessor {
185 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
186 const char* viewMatrix;
187 fViewMatrixUniform = args.fUniformHandler->addUniform(
188 kVertex_GrShaderFlag, kFloat3x3_GrSLType, "view_matrix", &viewMatrix);
189 args.fVertBuilder->declareGlobal(
190 GrShaderVar("M_", kFloat3x3_GrSLType, GrShaderVar::kOut_TypeModifier));
191 args.fVertBuilder->codeAppendf("M_ = %s;", viewMatrix);
192 // GrGLProgramBuilder will call writeTess*ShaderGLSL when it is compiling.
193 this->writeFragmentShader(args.fFragBuilder, args.fOutputColor, args.fOutputCoverage);
194 }
195 void writeFragmentShader(GrGLSLFPFragmentBuilder*, const char* color, const char* coverage);
196 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
197 const CoordTransformRange&) override {
198 pdman.setSkMatrix(fViewMatrixUniform,
199 proc.cast<TessellationTestRectShader>().fViewMatrix);
200 }
201 GrGLSLUniformHandler::UniformHandle fViewMatrixUniform;
202 };
203
204 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
205 return new Impl;
206 }
207
208 SkString getTessControlShaderGLSL(const char* versionAndExtensionDecls,
209 const GrShaderCaps&) const override;
210 SkString getTessEvaluationShaderGLSL(const char* versionAndExtensionDecls,
211 const GrShaderCaps&) const override;
212
213 const SkMatrix fViewMatrix;
214};
215
216SkString TessellationTestRectShader::getTessControlShaderGLSL(
217 const char* versionAndExtensionDecls, const GrShaderCaps& caps) const {
218 SkString code(versionAndExtensionDecls);
219 code.append(R"(
220 layout(vertices = 1) out;
221
222 in mat3 M_[];
223 out mat3 M[];
224
225 void main() {
226 M[gl_InvocationID] = M_[gl_InvocationID];
227 gl_TessLevelInner[0] = 8.0;
228 gl_TessLevelInner[1] = 2.0;
229 gl_TessLevelOuter[0] = 2.0;
230 gl_TessLevelOuter[1] = 8.0;
231 gl_TessLevelOuter[2] = 2.0;
232 gl_TessLevelOuter[3] = 8.0;
233 })");
234
235 return code;
236}
237
238SkString TessellationTestRectShader::getTessEvaluationShaderGLSL(
239 const char* versionAndExtensionDecls, const GrShaderCaps& caps) const {
240 SkString code(versionAndExtensionDecls);
241 code.appendf(R"(
242 layout(quads, equal_spacing, cw) in;
243
244 uniform vec4 sk_RTAdjust;
245
246 in mat3 M[];
247 out vec4 barycentric_coord;
248
249 void main() {
250 vec4 R = vec4(%f, %f, %f, %f);
251 vec2 localcoord = mix(R.xy, R.zw, gl_TessCoord.xy);
252 vec2 devcoord = (M[0] * vec3(localcoord, 1)).xy;
253 devcoord = round(devcoord - .5) + .5; // Make horz and vert lines on px bounds.
254 gl_Position = vec4(devcoord.xy * sk_RTAdjust.xz + sk_RTAdjust.yw, 0.0, 1.0);
255
256 float i = gl_TessCoord.x * 8.0;
257 i = abs(mod(i, 2.0) - 1.0);
258 if (gl_TessCoord.y == 0.0 || gl_TessCoord.y == 1.0) {
259 barycentric_coord = vec4(i, 1.0 - i, 0, 0);
260 } else {
261 barycentric_coord = vec4(0, 0, i, 1.0 - i);
262 }
263 })", kRect.left(), kRect.top(), kRect.right(), kRect.bottom());
264
265 return code;
266}
267
268void TessellationTestRectShader::Impl::writeFragmentShader(
269 GrGLSLFPFragmentBuilder* f, const char* color, const char* coverage) {
270 f->declareGlobal(GrShaderVar("barycentric_coord", kFloat4_GrSLType,
271 GrShaderVar::kIn_TypeModifier));
272 f->codeAppendf(R"(
273 float4 fwidths = fwidth(barycentric_coord);
274 half coverage = 0;
275 for (int i = 0; i < 4; ++i) {
276 if (fwidths[i] != 0) {
277 coverage = half(max(coverage, 1 - barycentric_coord[i]/fwidths[i]));
278 }
279 }
280 %s = half4(coverage, 0, coverage, 1);
281 %s = half4(1);)", color, coverage);
282}
283
284
285class TessellationTestOp : public GrDrawOp {
286 DEFINE_OP_CLASS_ID
287
288public:
289 TessellationTestOp(const SkMatrix& viewMatrix, const std::array<float, 3>* triPositions)
290 : GrDrawOp(ClassID()), fViewMatrix(viewMatrix), fTriPositions(triPositions) {
291 this->setBounds(SkRect::MakeIWH(kWidth, kHeight), HasAABloat::kNo, IsHairline::kNo);
292 }
293
294private:
295 const char* name() const override { return "TessellationTestOp"; }
296 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
297 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*,
298 bool hasMixedSampledCoverage, GrClampType) override {
299 return GrProcessorSet::EmptySetAnalysis();
300 }
301
302 void onPrepare(GrOpFlushState* flushState) override {
303 if (fTriPositions) {
304 if (void* vertexData = flushState->makeVertexSpace(sizeof(float) * 3, 3, &fVertexBuffer,
305 &fBaseVertex)) {
306 memcpy(vertexData, fTriPositions, sizeof(float) * 3 * 3);
307 }
308 }
309 }
310
311 void onExecute(GrOpFlushState* state, const SkRect& chainBounds) override {
312 GrPipeline pipeline(GrScissorTest::kDisabled, SkBlendMode::kSrc,
313 state->drawOpArgs().outputSwizzle());
314 GrPipeline::FixedDynamicState fixedDynamicState;
315
316 GrMesh mesh(GrPrimitiveType::kPatches);
317 std::unique_ptr<GrGeometryProcessor> shader;
318 if (fTriPositions) {
319 if (!fVertexBuffer) {
320 return;
321 }
322 mesh.setTessellationPatchVertexCount(3);
323 mesh.setNonIndexedNonInstanced(3);
324 mesh.setVertexData(fVertexBuffer, fBaseVertex);
325 shader = std::make_unique<TessellationTestTriShader>(fViewMatrix);
326 } else {
327 // Use a mismatched number of vertices in the input patch vs output.
328 // (The tessellation control shader will output one vertex per patch.)
329 mesh.setTessellationPatchVertexCount(5);
330 mesh.setNonIndexedNonInstanced(5);
331 shader = std::make_unique<TessellationTestRectShader>(fViewMatrix);
332 }
333
334 GrProgramInfo programInfo(state->proxy()->numSamples(), state->proxy()->numStencilSamples(),
335 state->proxy()->backendFormat(), state->view()->origin(),
336 &pipeline, shader.get(), &fixedDynamicState, nullptr, 0,
337 GrPrimitiveType::kPatches, mesh.tessellationPatchVertexCount());
338
339 state->opsRenderPass()->draw(programInfo, &mesh, 1, SkRect::MakeIWH(kWidth, kHeight));
340 }
341
342 const SkMatrix fViewMatrix;
343 const std::array<float, 3>* const fTriPositions;
344 sk_sp<const GrBuffer> fVertexBuffer;
345 int fBaseVertex = 0;
346};
347
348
349static SkPath build_outset_triangle(const std::array<float, 3>* tri) {
350 SkPath outset;
351 for (int i = 0; i < 3; ++i) {
352 SkPoint p = {tri[i][0], tri[i][1]};
353 SkPoint left = {tri[(i + 2) % 3][0], tri[(i + 2) % 3][1]};
354 SkPoint right = {tri[(i + 1) % 3][0], tri[(i + 1) % 3][1]};
355 SkPoint n0, n1;
356 n0.setNormalize(left.y() - p.y(), p.x() - left.x());
357 n1.setNormalize(p.y() - right.y(), right.x() - p.x());
358 p += (n0 + n1) * 3;
359 if (0 == i) {
360 outset.moveTo(p);
361 } else {
362 outset.lineTo(p);
363 }
364 }
365 return outset;
366}
367
368DrawResult TessellationGM::onDraw(GrContext* ctx, GrRenderTargetContext* rtc, SkCanvas* canvas,
369 SkString* errorMsg) {
370 if (!ctx->priv().caps()->shaderCaps()->tessellationSupport()) {
371 *errorMsg = "Requires GPU tessellation support.";
372 return DrawResult::kSkip;
373 }
374 if (!ctx->priv().caps()->shaderCaps()->shaderDerivativeSupport()) {
375 *errorMsg = "Requires shader derivatives."
376 "(These are expected to always be present when there is tessellation!!)";
377 return DrawResult::kFail;
378 }
379
380 canvas->clear(SK_ColorBLACK);
381 SkPaint borderPaint;
382 borderPaint.setColor4f({0,1,1,1});
383 borderPaint.setAntiAlias(true);
384 canvas->drawPath(build_outset_triangle(kTri1), borderPaint);
385 canvas->drawPath(build_outset_triangle(kTri2), borderPaint);
386
387 borderPaint.setColor4f({1,0,1,1});
388 canvas->drawRect(kRect.makeOutset(1.5f, 1.5f), borderPaint);
389
390 GrOpMemoryPool* pool = ctx->priv().opMemoryPool();
391 rtc->priv().testingOnly_addDrawOp(
392 pool->allocate<TessellationTestOp>(canvas->getTotalMatrix(), kTri1));
393 rtc->priv().testingOnly_addDrawOp(
394 pool->allocate<TessellationTestOp>(canvas->getTotalMatrix(), kTri2));
395 rtc->priv().testingOnly_addDrawOp(
396 pool->allocate<TessellationTestOp>(canvas->getTotalMatrix(), nullptr));
397
398 return skiagm::DrawResult::kOk;
399}
400
401DEF_GM( return new TessellationGM(); )
402
403}