blob: bb92397933bc6b5b7d558a65b355a31e0fc175cb [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"
Chris Dalton5a2f9622019-12-27 14:56:38 -070013#include "src/gpu/GrOpFlushState.h"
14#include "src/gpu/GrOpsRenderPass.h"
15#include "src/gpu/GrPipeline.h"
16#include "src/gpu/GrPrimitiveProcessor.h"
17#include "src/gpu/GrProgramInfo.h"
18#include "src/gpu/GrRecordingContextPriv.h"
19#include "src/gpu/GrRenderTargetContext.h"
20#include "src/gpu/GrRenderTargetContextPriv.h"
21#include "src/gpu/GrShaderCaps.h"
22#include "src/gpu/GrShaderVar.h"
23#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
24#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
25#include "src/gpu/glsl/GrGLSLPrimitiveProcessor.h"
26#include "src/gpu/glsl/GrGLSLVarying.h"
27#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
28#include "src/gpu/ops/GrDrawOp.h"
29
30namespace skiagm {
31
32constexpr static GrGeometryProcessor::Attribute kPositionAttrib =
33 {"position", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
34
35constexpr static std::array<float, 3> kTri1[3] = {
36 {20.5f,20.5f,1}, {170.5f,280.5f,4}, {320.5f,20.5f,1}};
37constexpr static std::array<float, 3> kTri2[3] = {
38 {640.5f,280.5f,3}, {490.5f,20.5f,1}, {340.5f,280.5f,6}};
39constexpr static SkRect kRect = {20.5f, 340.5f, 640.5f, 480.5f};
40
41constexpr static int kWidth = (int)kRect.fRight + 21;
42constexpr static int kHeight = (int)kRect.fBottom + 21;
43
44/**
45 * This is a GPU-backend specific test. It ensures that tessellation works as expected by drawing
46 * several triangles. The test passes as long as the triangle tessellations match the reference
47 * images on gold.
48 */
49class TessellationGM : public GpuGM {
50 SkString onShortName() override { return SkString("tessellation"); }
51 SkISize onISize() override { return {kWidth, kHeight}; }
52 DrawResult onDraw(GrContext*, GrRenderTargetContext*, SkCanvas*, SkString*) override;
53};
54
55
56class TessellationTestTriShader : public GrGeometryProcessor {
57public:
58 TessellationTestTriShader(const SkMatrix& viewMatrix)
59 : GrGeometryProcessor(kTessellationTestTriShader_ClassID), fViewMatrix(viewMatrix) {
60 this->setVertexAttributes(&kPositionAttrib, 1);
61 this->setWillUseTessellationShaders();
62 }
63
Robert Phillips709e2402020-03-23 18:29:16 +000064private:
65 const char* name() const final { return "TessellationTestTriShader"; }
66 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {}
67
Chris Dalton5a2f9622019-12-27 14:56:38 -070068 class Impl : public GrGLSLGeometryProcessor {
69 void onEmitCode(EmitArgs& args, GrGPArgs*) override {
70 args.fVaryingHandler->emitAttributes(args.fGP.cast<TessellationTestTriShader>());
71 const char* viewMatrix;
72 fViewMatrixUniform = args.fUniformHandler->addUniform(
Ethan Nicholas16464c32020-04-06 13:53:05 -040073 nullptr, kVertex_GrShaderFlag, kFloat3x3_GrSLType, "view_matrix", &viewMatrix);
Chris Dalton5a2f9622019-12-27 14:56:38 -070074 args.fVertBuilder->declareGlobal(
Ben Wagnerdabd98c2020-03-25 15:17:18 -040075 GrShaderVar("P_", kFloat3_GrSLType, GrShaderVar::TypeModifier::Out));
Chris Dalton5a2f9622019-12-27 14:56:38 -070076 args.fVertBuilder->codeAppendf(R"(
77 P_.xy = (%s * float3(position.xy, 1)).xy;
78 P_.z = position.z;)", viewMatrix);
79 // GrGLProgramBuilder will call writeTess*ShaderGLSL when it is compiling.
80 this->writeFragmentShader(args.fFragBuilder, args.fOutputColor, args.fOutputCoverage);
81 }
82 void writeFragmentShader(GrGLSLFPFragmentBuilder*, const char* color, const char* coverage);
83 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
84 const CoordTransformRange&) override {
85 pdman.setSkMatrix(fViewMatrixUniform,
86 proc.cast<TessellationTestTriShader>().fViewMatrix);
87 }
88 GrGLSLUniformHandler::UniformHandle fViewMatrixUniform;
89 };
90
91 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
92 return new Impl;
93 }
94
Chris Dalton54c90932020-06-24 10:51:52 -060095 SkString getTessControlShaderGLSL(const GrGLSLPrimitiveProcessor*,
96 const char* versionAndExtensionDecls,
97 const GrGLSLUniformHandler&,
Chris Dalton5a2f9622019-12-27 14:56:38 -070098 const GrShaderCaps&) const override;
Chris Dalton54c90932020-06-24 10:51:52 -060099 SkString getTessEvaluationShaderGLSL(const GrGLSLPrimitiveProcessor*,
100 const char* versionAndExtensionDecls,
101 const GrGLSLUniformHandler&,
Chris Dalton5a2f9622019-12-27 14:56:38 -0700102 const GrShaderCaps&) const override;
103
104 const SkMatrix fViewMatrix;
105};
106
107SkString TessellationTestTriShader::getTessControlShaderGLSL(
Chris Dalton54c90932020-06-24 10:51:52 -0600108 const GrGLSLPrimitiveProcessor*, const char* versionAndExtensionDecls,
109 const GrGLSLUniformHandler&, const GrShaderCaps&) const {
Chris Dalton5a2f9622019-12-27 14:56:38 -0700110 SkString code(versionAndExtensionDecls);
111 code.append(R"(
112 layout(vertices = 3) out;
113
114 in vec3 P_[];
115 out vec3 P[];
116
117 void main() {
118 P[gl_InvocationID] = P_[gl_InvocationID];
119 gl_TessLevelOuter[gl_InvocationID] = P_[gl_InvocationID].z;
120 gl_TessLevelInner[0] = 2.0;
121 })");
122
123 return code;
124}
125
126SkString TessellationTestTriShader::getTessEvaluationShaderGLSL(
Chris Dalton54c90932020-06-24 10:51:52 -0600127 const GrGLSLPrimitiveProcessor*, const char* versionAndExtensionDecls,
128 const GrGLSLUniformHandler&, const GrShaderCaps&) const {
Chris Dalton5a2f9622019-12-27 14:56:38 -0700129 SkString code(versionAndExtensionDecls);
130 code.append(R"(
131 layout(triangles, equal_spacing, cw) in;
132
133 uniform vec4 sk_RTAdjust;
134
135 in vec3 P[];
136 out vec3 barycentric_coord;
137
138 void main() {
139 vec2 devcoord = mat3x2(P[0].xy, P[1].xy, P[2].xy) * gl_TessCoord.xyz;
140 devcoord = round(devcoord - .5) + .5; // Make horz and vert lines on px bounds.
141 gl_Position = vec4(devcoord.xy * sk_RTAdjust.xz + sk_RTAdjust.yw, 0.0, 1.0);
142
143 float i = 0.0;
144 if (gl_TessCoord.y == 0.0) {
145 i += gl_TessCoord.z * P[1].z;
146 } else {
147 i += P[1].z;
148 if (gl_TessCoord.x == 0.0) {
149 i += gl_TessCoord.y * P[0].z;
150 } else {
151 i += P[0].z;
Chris Dalton8dae7eb2019-12-27 22:20:55 -0700152 if (gl_TessCoord.z == 0.0) {
153 i += gl_TessCoord.x * P[2].z;
154 } else {
155 barycentric_coord = vec3(0, 1, 0);
156 return;
157 }
Chris Dalton5a2f9622019-12-27 14:56:38 -0700158 }
159 }
Chris Dalton8dae7eb2019-12-27 22:20:55 -0700160 i = abs(mod(i, 2.0) - 1.0);
161 barycentric_coord = vec3(i, 0, 1.0 - i);
162 })");
Chris Dalton5a2f9622019-12-27 14:56:38 -0700163
164 return code;
165}
166
167void TessellationTestTriShader::Impl::writeFragmentShader(
168 GrGLSLFPFragmentBuilder* f, const char* color, const char* coverage) {
169 f->declareGlobal(
Ben Wagnerdabd98c2020-03-25 15:17:18 -0400170 GrShaderVar("barycentric_coord", kFloat3_GrSLType, GrShaderVar::TypeModifier::In));
Chris Dalton5a2f9622019-12-27 14:56:38 -0700171 f->codeAppendf(R"(
172 half3 d = half3(1 - barycentric_coord/fwidth(barycentric_coord));
173 half coverage = max(max(d.x, d.y), d.z);
174 %s = half4(0, coverage, coverage, 1);
175 %s = half4(1);)", color, coverage);
176}
177
178class TessellationTestRectShader : public GrGeometryProcessor {
179public:
180 TessellationTestRectShader(const SkMatrix& viewMatrix)
181 : GrGeometryProcessor(kTessellationTestTriShader_ClassID), fViewMatrix(viewMatrix) {
182 this->setWillUseTessellationShaders();
183 }
184
Robert Phillips709e2402020-03-23 18:29:16 +0000185private:
186 const char* name() const final { return "TessellationTestRectShader"; }
187 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {}
188
Chris Dalton5a2f9622019-12-27 14:56:38 -0700189 class Impl : public GrGLSLGeometryProcessor {
190 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
191 const char* viewMatrix;
192 fViewMatrixUniform = args.fUniformHandler->addUniform(
Ethan Nicholas16464c32020-04-06 13:53:05 -0400193 nullptr, kVertex_GrShaderFlag, kFloat3x3_GrSLType, "view_matrix", &viewMatrix);
Chris Dalton5a2f9622019-12-27 14:56:38 -0700194 args.fVertBuilder->declareGlobal(
Ben Wagnerdabd98c2020-03-25 15:17:18 -0400195 GrShaderVar("M_", kFloat3x3_GrSLType, GrShaderVar::TypeModifier::Out));
Chris Dalton5a2f9622019-12-27 14:56:38 -0700196 args.fVertBuilder->codeAppendf("M_ = %s;", viewMatrix);
197 // GrGLProgramBuilder will call writeTess*ShaderGLSL when it is compiling.
198 this->writeFragmentShader(args.fFragBuilder, args.fOutputColor, args.fOutputCoverage);
199 }
200 void writeFragmentShader(GrGLSLFPFragmentBuilder*, const char* color, const char* coverage);
201 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
202 const CoordTransformRange&) override {
203 pdman.setSkMatrix(fViewMatrixUniform,
204 proc.cast<TessellationTestRectShader>().fViewMatrix);
205 }
206 GrGLSLUniformHandler::UniformHandle fViewMatrixUniform;
207 };
208
209 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
210 return new Impl;
211 }
212
Chris Dalton54c90932020-06-24 10:51:52 -0600213 SkString getTessControlShaderGLSL(const GrGLSLPrimitiveProcessor*,
214 const char* versionAndExtensionDecls,
215 const GrGLSLUniformHandler&,
Chris Dalton5a2f9622019-12-27 14:56:38 -0700216 const GrShaderCaps&) const override;
Chris Dalton54c90932020-06-24 10:51:52 -0600217 SkString getTessEvaluationShaderGLSL(const GrGLSLPrimitiveProcessor*,
218 const char* versionAndExtensionDecls,
219 const GrGLSLUniformHandler&,
Chris Dalton5a2f9622019-12-27 14:56:38 -0700220 const GrShaderCaps&) const override;
221
222 const SkMatrix fViewMatrix;
223};
224
225SkString TessellationTestRectShader::getTessControlShaderGLSL(
Chris Dalton54c90932020-06-24 10:51:52 -0600226 const GrGLSLPrimitiveProcessor*, const char* versionAndExtensionDecls,
227 const GrGLSLUniformHandler&, const GrShaderCaps& caps) const {
Chris Dalton5a2f9622019-12-27 14:56:38 -0700228 SkString code(versionAndExtensionDecls);
229 code.append(R"(
230 layout(vertices = 1) out;
231
232 in mat3 M_[];
233 out mat3 M[];
234
235 void main() {
236 M[gl_InvocationID] = M_[gl_InvocationID];
237 gl_TessLevelInner[0] = 8.0;
238 gl_TessLevelInner[1] = 2.0;
239 gl_TessLevelOuter[0] = 2.0;
240 gl_TessLevelOuter[1] = 8.0;
241 gl_TessLevelOuter[2] = 2.0;
242 gl_TessLevelOuter[3] = 8.0;
243 })");
244
245 return code;
246}
247
248SkString TessellationTestRectShader::getTessEvaluationShaderGLSL(
Chris Dalton54c90932020-06-24 10:51:52 -0600249 const GrGLSLPrimitiveProcessor*, const char* versionAndExtensionDecls,
250 const GrGLSLUniformHandler&, const GrShaderCaps& caps) const {
Chris Dalton5a2f9622019-12-27 14:56:38 -0700251 SkString code(versionAndExtensionDecls);
252 code.appendf(R"(
253 layout(quads, equal_spacing, cw) in;
254
255 uniform vec4 sk_RTAdjust;
256
257 in mat3 M[];
258 out vec4 barycentric_coord;
259
260 void main() {
261 vec4 R = vec4(%f, %f, %f, %f);
262 vec2 localcoord = mix(R.xy, R.zw, gl_TessCoord.xy);
263 vec2 devcoord = (M[0] * vec3(localcoord, 1)).xy;
264 devcoord = round(devcoord - .5) + .5; // Make horz and vert lines on px bounds.
265 gl_Position = vec4(devcoord.xy * sk_RTAdjust.xz + sk_RTAdjust.yw, 0.0, 1.0);
266
267 float i = gl_TessCoord.x * 8.0;
268 i = abs(mod(i, 2.0) - 1.0);
269 if (gl_TessCoord.y == 0.0 || gl_TessCoord.y == 1.0) {
270 barycentric_coord = vec4(i, 1.0 - i, 0, 0);
271 } else {
272 barycentric_coord = vec4(0, 0, i, 1.0 - i);
273 }
274 })", kRect.left(), kRect.top(), kRect.right(), kRect.bottom());
275
276 return code;
277}
278
279void TessellationTestRectShader::Impl::writeFragmentShader(
280 GrGLSLFPFragmentBuilder* f, const char* color, const char* coverage) {
281 f->declareGlobal(GrShaderVar("barycentric_coord", kFloat4_GrSLType,
Ben Wagnerdabd98c2020-03-25 15:17:18 -0400282 GrShaderVar::TypeModifier::In));
Chris Dalton5a2f9622019-12-27 14:56:38 -0700283 f->codeAppendf(R"(
284 float4 fwidths = fwidth(barycentric_coord);
285 half coverage = 0;
286 for (int i = 0; i < 4; ++i) {
287 if (fwidths[i] != 0) {
288 coverage = half(max(coverage, 1 - barycentric_coord[i]/fwidths[i]));
289 }
290 }
291 %s = half4(coverage, 0, coverage, 1);
292 %s = half4(1);)", color, coverage);
293}
294
295
296class TessellationTestOp : public GrDrawOp {
297 DEFINE_OP_CLASS_ID
298
299public:
300 TessellationTestOp(const SkMatrix& viewMatrix, const std::array<float, 3>* triPositions)
Robert Phillips709e2402020-03-23 18:29:16 +0000301 : GrDrawOp(ClassID()), fViewMatrix(viewMatrix), fTriPositions(triPositions) {
Chris Dalton5a2f9622019-12-27 14:56:38 -0700302 this->setBounds(SkRect::MakeIWH(kWidth, kHeight), HasAABloat::kNo, IsHairline::kNo);
303 }
304
305private:
306 const char* name() const override { return "TessellationTestOp"; }
307 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
Robert Phillips709e2402020-03-23 18:29:16 +0000308 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*,
309 bool hasMixedSampledCoverage, GrClampType) override {
310 return GrProcessorSet::EmptySetAnalysis();
Chris Dalton5a2f9622019-12-27 14:56:38 -0700311 }
312
Robert Phillips709e2402020-03-23 18:29:16 +0000313 void onPrePrepare(GrRecordingContext*,
Brian Salomon8afde5f2020-04-01 16:22:00 -0400314 const GrSurfaceProxyView* writeView,
Robert Phillips709e2402020-03-23 18:29:16 +0000315 GrAppliedClip*,
316 const GrXferProcessor::DstProxyView&) override {}
Robert Phillipsc655c3a2020-03-18 13:23:45 -0400317
Chris Dalton5a2f9622019-12-27 14:56:38 -0700318 void onPrepare(GrOpFlushState* flushState) override {
319 if (fTriPositions) {
320 if (void* vertexData = flushState->makeVertexSpace(sizeof(float) * 3, 3, &fVertexBuffer,
321 &fBaseVertex)) {
322 memcpy(vertexData, fTriPositions, sizeof(float) * 3 * 3);
323 }
324 }
325 }
326
327 void onExecute(GrOpFlushState* state, const SkRect& chainBounds) override {
Robert Phillips709e2402020-03-23 18:29:16 +0000328 GrPipeline pipeline(GrScissorTest::kDisabled, SkBlendMode::kSrc,
Brian Salomon982f5462020-03-30 12:52:33 -0400329 state->drawOpArgs().writeSwizzle());
Robert Phillips709e2402020-03-23 18:29:16 +0000330 int tessellationPatchVertexCount;
331 std::unique_ptr<GrGeometryProcessor> shader;
332 if (fTriPositions) {
333 if (!fVertexBuffer) {
334 return;
335 }
336 tessellationPatchVertexCount = 3;
337 shader = std::make_unique<TessellationTestTriShader>(fViewMatrix);
338 } else {
339 // Use a mismatched number of vertices in the input patch vs output.
340 // (The tessellation control shader will output one vertex per patch.)
341 tessellationPatchVertexCount = 5;
342 shader = std::make_unique<TessellationTestRectShader>(fViewMatrix);
Chris Dalton5a2f9622019-12-27 14:56:38 -0700343 }
344
Robert Phillips709e2402020-03-23 18:29:16 +0000345 GrProgramInfo programInfo(state->proxy()->numSamples(), state->proxy()->numStencilSamples(),
Brian Salomon8afde5f2020-04-01 16:22:00 -0400346 state->proxy()->backendFormat(), state->writeView()->origin(),
Robert Phillips709e2402020-03-23 18:29:16 +0000347 &pipeline, shader.get(), GrPrimitiveType::kPatches,
348 tessellationPatchVertexCount);
Chris Dalton5a2f9622019-12-27 14:56:38 -0700349
Robert Phillips709e2402020-03-23 18:29:16 +0000350 state->bindPipeline(programInfo, SkRect::MakeIWH(kWidth, kHeight));
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600351 state->bindBuffers(nullptr, nullptr, fVertexBuffer.get());
Robert Phillips709e2402020-03-23 18:29:16 +0000352 state->draw(tessellationPatchVertexCount, fBaseVertex);
Chris Dalton5a2f9622019-12-27 14:56:38 -0700353 }
354
355 const SkMatrix fViewMatrix;
356 const std::array<float, 3>* const fTriPositions;
357 sk_sp<const GrBuffer> fVertexBuffer;
358 int fBaseVertex = 0;
Chris Dalton5a2f9622019-12-27 14:56:38 -0700359};
360
361
362static SkPath build_outset_triangle(const std::array<float, 3>* tri) {
363 SkPath outset;
364 for (int i = 0; i < 3; ++i) {
365 SkPoint p = {tri[i][0], tri[i][1]};
366 SkPoint left = {tri[(i + 2) % 3][0], tri[(i + 2) % 3][1]};
367 SkPoint right = {tri[(i + 1) % 3][0], tri[(i + 1) % 3][1]};
368 SkPoint n0, n1;
369 n0.setNormalize(left.y() - p.y(), p.x() - left.x());
370 n1.setNormalize(p.y() - right.y(), right.x() - p.x());
371 p += (n0 + n1) * 3;
372 if (0 == i) {
373 outset.moveTo(p);
374 } else {
375 outset.lineTo(p);
376 }
377 }
378 return outset;
379}
380
381DrawResult TessellationGM::onDraw(GrContext* ctx, GrRenderTargetContext* rtc, SkCanvas* canvas,
382 SkString* errorMsg) {
383 if (!ctx->priv().caps()->shaderCaps()->tessellationSupport()) {
384 *errorMsg = "Requires GPU tessellation support.";
385 return DrawResult::kSkip;
386 }
387 if (!ctx->priv().caps()->shaderCaps()->shaderDerivativeSupport()) {
388 *errorMsg = "Requires shader derivatives."
389 "(These are expected to always be present when there is tessellation!!)";
390 return DrawResult::kFail;
391 }
392
393 canvas->clear(SK_ColorBLACK);
394 SkPaint borderPaint;
395 borderPaint.setColor4f({0,1,1,1});
396 borderPaint.setAntiAlias(true);
397 canvas->drawPath(build_outset_triangle(kTri1), borderPaint);
398 canvas->drawPath(build_outset_triangle(kTri2), borderPaint);
399
400 borderPaint.setColor4f({1,0,1,1});
401 canvas->drawRect(kRect.makeOutset(1.5f, 1.5f), borderPaint);
402
403 GrOpMemoryPool* pool = ctx->priv().opMemoryPool();
404 rtc->priv().testingOnly_addDrawOp(
405 pool->allocate<TessellationTestOp>(canvas->getTotalMatrix(), kTri1));
406 rtc->priv().testingOnly_addDrawOp(
407 pool->allocate<TessellationTestOp>(canvas->getTotalMatrix(), kTri2));
408 rtc->priv().testingOnly_addDrawOp(
409 pool->allocate<TessellationTestOp>(canvas->getTotalMatrix(), nullptr));
410
411 return skiagm::DrawResult::kOk;
412}
413
414DEF_GM( return new TessellationGM(); )
415
416}