blob: 6a39c3dbd078342c04b60d88e8c88b57347e03bb [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(
73 kVertex_GrShaderFlag, kFloat3x3_GrSLType, "view_matrix", &viewMatrix);
74 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
95 SkString getTessControlShaderGLSL(const char* versionAndExtensionDecls,
96 const GrShaderCaps&) const override;
97 SkString getTessEvaluationShaderGLSL(const char* versionAndExtensionDecls,
98 const GrShaderCaps&) const override;
99
100 const SkMatrix fViewMatrix;
101};
102
103SkString TessellationTestTriShader::getTessControlShaderGLSL(
104 const char* versionAndExtensionDecls, const GrShaderCaps&) const {
105 SkString code(versionAndExtensionDecls);
106 code.append(R"(
107 layout(vertices = 3) out;
108
109 in vec3 P_[];
110 out vec3 P[];
111
112 void main() {
113 P[gl_InvocationID] = P_[gl_InvocationID];
114 gl_TessLevelOuter[gl_InvocationID] = P_[gl_InvocationID].z;
115 gl_TessLevelInner[0] = 2.0;
116 })");
117
118 return code;
119}
120
121SkString TessellationTestTriShader::getTessEvaluationShaderGLSL(
122 const char* versionAndExtensionDecls, const GrShaderCaps&) const {
123 SkString code(versionAndExtensionDecls);
124 code.append(R"(
125 layout(triangles, equal_spacing, cw) in;
126
127 uniform vec4 sk_RTAdjust;
128
129 in vec3 P[];
130 out vec3 barycentric_coord;
131
132 void main() {
133 vec2 devcoord = mat3x2(P[0].xy, P[1].xy, P[2].xy) * gl_TessCoord.xyz;
134 devcoord = round(devcoord - .5) + .5; // Make horz and vert lines on px bounds.
135 gl_Position = vec4(devcoord.xy * sk_RTAdjust.xz + sk_RTAdjust.yw, 0.0, 1.0);
136
137 float i = 0.0;
138 if (gl_TessCoord.y == 0.0) {
139 i += gl_TessCoord.z * P[1].z;
140 } else {
141 i += P[1].z;
142 if (gl_TessCoord.x == 0.0) {
143 i += gl_TessCoord.y * P[0].z;
144 } else {
145 i += P[0].z;
Chris Dalton8dae7eb2019-12-27 22:20:55 -0700146 if (gl_TessCoord.z == 0.0) {
147 i += gl_TessCoord.x * P[2].z;
148 } else {
149 barycentric_coord = vec3(0, 1, 0);
150 return;
151 }
Chris Dalton5a2f9622019-12-27 14:56:38 -0700152 }
153 }
Chris Dalton8dae7eb2019-12-27 22:20:55 -0700154 i = abs(mod(i, 2.0) - 1.0);
155 barycentric_coord = vec3(i, 0, 1.0 - i);
156 })");
Chris Dalton5a2f9622019-12-27 14:56:38 -0700157
158 return code;
159}
160
161void TessellationTestTriShader::Impl::writeFragmentShader(
162 GrGLSLFPFragmentBuilder* f, const char* color, const char* coverage) {
163 f->declareGlobal(
Ben Wagnerdabd98c2020-03-25 15:17:18 -0400164 GrShaderVar("barycentric_coord", kFloat3_GrSLType, GrShaderVar::TypeModifier::In));
Chris Dalton5a2f9622019-12-27 14:56:38 -0700165 f->codeAppendf(R"(
166 half3 d = half3(1 - barycentric_coord/fwidth(barycentric_coord));
167 half coverage = max(max(d.x, d.y), d.z);
168 %s = half4(0, coverage, coverage, 1);
169 %s = half4(1);)", color, coverage);
170}
171
172class TessellationTestRectShader : public GrGeometryProcessor {
173public:
174 TessellationTestRectShader(const SkMatrix& viewMatrix)
175 : GrGeometryProcessor(kTessellationTestTriShader_ClassID), fViewMatrix(viewMatrix) {
176 this->setWillUseTessellationShaders();
177 }
178
Robert Phillips709e2402020-03-23 18:29:16 +0000179private:
180 const char* name() const final { return "TessellationTestRectShader"; }
181 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {}
182
Chris Dalton5a2f9622019-12-27 14:56:38 -0700183 class Impl : public GrGLSLGeometryProcessor {
184 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
185 const char* viewMatrix;
186 fViewMatrixUniform = args.fUniformHandler->addUniform(
187 kVertex_GrShaderFlag, kFloat3x3_GrSLType, "view_matrix", &viewMatrix);
188 args.fVertBuilder->declareGlobal(
Ben Wagnerdabd98c2020-03-25 15:17:18 -0400189 GrShaderVar("M_", kFloat3x3_GrSLType, GrShaderVar::TypeModifier::Out));
Chris Dalton5a2f9622019-12-27 14:56:38 -0700190 args.fVertBuilder->codeAppendf("M_ = %s;", viewMatrix);
191 // GrGLProgramBuilder will call writeTess*ShaderGLSL when it is compiling.
192 this->writeFragmentShader(args.fFragBuilder, args.fOutputColor, args.fOutputCoverage);
193 }
194 void writeFragmentShader(GrGLSLFPFragmentBuilder*, const char* color, const char* coverage);
195 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
196 const CoordTransformRange&) override {
197 pdman.setSkMatrix(fViewMatrixUniform,
198 proc.cast<TessellationTestRectShader>().fViewMatrix);
199 }
200 GrGLSLUniformHandler::UniformHandle fViewMatrixUniform;
201 };
202
203 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
204 return new Impl;
205 }
206
207 SkString getTessControlShaderGLSL(const char* versionAndExtensionDecls,
208 const GrShaderCaps&) const override;
209 SkString getTessEvaluationShaderGLSL(const char* versionAndExtensionDecls,
210 const GrShaderCaps&) const override;
211
212 const SkMatrix fViewMatrix;
213};
214
215SkString TessellationTestRectShader::getTessControlShaderGLSL(
216 const char* versionAndExtensionDecls, const GrShaderCaps& caps) const {
217 SkString code(versionAndExtensionDecls);
218 code.append(R"(
219 layout(vertices = 1) out;
220
221 in mat3 M_[];
222 out mat3 M[];
223
224 void main() {
225 M[gl_InvocationID] = M_[gl_InvocationID];
226 gl_TessLevelInner[0] = 8.0;
227 gl_TessLevelInner[1] = 2.0;
228 gl_TessLevelOuter[0] = 2.0;
229 gl_TessLevelOuter[1] = 8.0;
230 gl_TessLevelOuter[2] = 2.0;
231 gl_TessLevelOuter[3] = 8.0;
232 })");
233
234 return code;
235}
236
237SkString TessellationTestRectShader::getTessEvaluationShaderGLSL(
238 const char* versionAndExtensionDecls, const GrShaderCaps& caps) const {
239 SkString code(versionAndExtensionDecls);
240 code.appendf(R"(
241 layout(quads, equal_spacing, cw) in;
242
243 uniform vec4 sk_RTAdjust;
244
245 in mat3 M[];
246 out vec4 barycentric_coord;
247
248 void main() {
249 vec4 R = vec4(%f, %f, %f, %f);
250 vec2 localcoord = mix(R.xy, R.zw, gl_TessCoord.xy);
251 vec2 devcoord = (M[0] * vec3(localcoord, 1)).xy;
252 devcoord = round(devcoord - .5) + .5; // Make horz and vert lines on px bounds.
253 gl_Position = vec4(devcoord.xy * sk_RTAdjust.xz + sk_RTAdjust.yw, 0.0, 1.0);
254
255 float i = gl_TessCoord.x * 8.0;
256 i = abs(mod(i, 2.0) - 1.0);
257 if (gl_TessCoord.y == 0.0 || gl_TessCoord.y == 1.0) {
258 barycentric_coord = vec4(i, 1.0 - i, 0, 0);
259 } else {
260 barycentric_coord = vec4(0, 0, i, 1.0 - i);
261 }
262 })", kRect.left(), kRect.top(), kRect.right(), kRect.bottom());
263
264 return code;
265}
266
267void TessellationTestRectShader::Impl::writeFragmentShader(
268 GrGLSLFPFragmentBuilder* f, const char* color, const char* coverage) {
269 f->declareGlobal(GrShaderVar("barycentric_coord", kFloat4_GrSLType,
Ben Wagnerdabd98c2020-03-25 15:17:18 -0400270 GrShaderVar::TypeModifier::In));
Chris Dalton5a2f9622019-12-27 14:56:38 -0700271 f->codeAppendf(R"(
272 float4 fwidths = fwidth(barycentric_coord);
273 half coverage = 0;
274 for (int i = 0; i < 4; ++i) {
275 if (fwidths[i] != 0) {
276 coverage = half(max(coverage, 1 - barycentric_coord[i]/fwidths[i]));
277 }
278 }
279 %s = half4(coverage, 0, coverage, 1);
280 %s = half4(1);)", color, coverage);
281}
282
283
284class TessellationTestOp : public GrDrawOp {
285 DEFINE_OP_CLASS_ID
286
287public:
288 TessellationTestOp(const SkMatrix& viewMatrix, const std::array<float, 3>* triPositions)
Robert Phillips709e2402020-03-23 18:29:16 +0000289 : GrDrawOp(ClassID()), fViewMatrix(viewMatrix), fTriPositions(triPositions) {
Chris Dalton5a2f9622019-12-27 14:56:38 -0700290 this->setBounds(SkRect::MakeIWH(kWidth, kHeight), HasAABloat::kNo, IsHairline::kNo);
291 }
292
293private:
294 const char* name() const override { return "TessellationTestOp"; }
295 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
Robert Phillips709e2402020-03-23 18:29:16 +0000296 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*,
297 bool hasMixedSampledCoverage, GrClampType) override {
298 return GrProcessorSet::EmptySetAnalysis();
Chris Dalton5a2f9622019-12-27 14:56:38 -0700299 }
300
Robert Phillips709e2402020-03-23 18:29:16 +0000301 void onPrePrepare(GrRecordingContext*,
Brian Salomon8afde5f2020-04-01 16:22:00 -0400302 const GrSurfaceProxyView* writeView,
Robert Phillips709e2402020-03-23 18:29:16 +0000303 GrAppliedClip*,
304 const GrXferProcessor::DstProxyView&) override {}
Robert Phillipsc655c3a2020-03-18 13:23:45 -0400305
Chris Dalton5a2f9622019-12-27 14:56:38 -0700306 void onPrepare(GrOpFlushState* flushState) override {
307 if (fTriPositions) {
308 if (void* vertexData = flushState->makeVertexSpace(sizeof(float) * 3, 3, &fVertexBuffer,
309 &fBaseVertex)) {
310 memcpy(vertexData, fTriPositions, sizeof(float) * 3 * 3);
311 }
312 }
313 }
314
315 void onExecute(GrOpFlushState* state, const SkRect& chainBounds) override {
Robert Phillips709e2402020-03-23 18:29:16 +0000316 GrPipeline pipeline(GrScissorTest::kDisabled, SkBlendMode::kSrc,
Brian Salomon982f5462020-03-30 12:52:33 -0400317 state->drawOpArgs().writeSwizzle());
Robert Phillips709e2402020-03-23 18:29:16 +0000318 int tessellationPatchVertexCount;
319 std::unique_ptr<GrGeometryProcessor> shader;
320 if (fTriPositions) {
321 if (!fVertexBuffer) {
322 return;
323 }
324 tessellationPatchVertexCount = 3;
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 tessellationPatchVertexCount = 5;
330 shader = std::make_unique<TessellationTestRectShader>(fViewMatrix);
Chris Dalton5a2f9622019-12-27 14:56:38 -0700331 }
332
Robert Phillips709e2402020-03-23 18:29:16 +0000333 GrProgramInfo programInfo(state->proxy()->numSamples(), state->proxy()->numStencilSamples(),
Brian Salomon8afde5f2020-04-01 16:22:00 -0400334 state->proxy()->backendFormat(), state->writeView()->origin(),
Robert Phillips709e2402020-03-23 18:29:16 +0000335 &pipeline, shader.get(), GrPrimitiveType::kPatches,
336 tessellationPatchVertexCount);
Chris Dalton5a2f9622019-12-27 14:56:38 -0700337
Robert Phillips709e2402020-03-23 18:29:16 +0000338 state->bindPipeline(programInfo, SkRect::MakeIWH(kWidth, kHeight));
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600339 state->bindBuffers(nullptr, nullptr, fVertexBuffer.get());
Robert Phillips709e2402020-03-23 18:29:16 +0000340 state->draw(tessellationPatchVertexCount, fBaseVertex);
Chris Dalton5a2f9622019-12-27 14:56:38 -0700341 }
342
343 const SkMatrix fViewMatrix;
344 const std::array<float, 3>* const fTriPositions;
345 sk_sp<const GrBuffer> fVertexBuffer;
346 int fBaseVertex = 0;
Chris Dalton5a2f9622019-12-27 14:56:38 -0700347};
348
349
350static SkPath build_outset_triangle(const std::array<float, 3>* tri) {
351 SkPath outset;
352 for (int i = 0; i < 3; ++i) {
353 SkPoint p = {tri[i][0], tri[i][1]};
354 SkPoint left = {tri[(i + 2) % 3][0], tri[(i + 2) % 3][1]};
355 SkPoint right = {tri[(i + 1) % 3][0], tri[(i + 1) % 3][1]};
356 SkPoint n0, n1;
357 n0.setNormalize(left.y() - p.y(), p.x() - left.x());
358 n1.setNormalize(p.y() - right.y(), right.x() - p.x());
359 p += (n0 + n1) * 3;
360 if (0 == i) {
361 outset.moveTo(p);
362 } else {
363 outset.lineTo(p);
364 }
365 }
366 return outset;
367}
368
369DrawResult TessellationGM::onDraw(GrContext* ctx, GrRenderTargetContext* rtc, SkCanvas* canvas,
370 SkString* errorMsg) {
371 if (!ctx->priv().caps()->shaderCaps()->tessellationSupport()) {
372 *errorMsg = "Requires GPU tessellation support.";
373 return DrawResult::kSkip;
374 }
375 if (!ctx->priv().caps()->shaderCaps()->shaderDerivativeSupport()) {
376 *errorMsg = "Requires shader derivatives."
377 "(These are expected to always be present when there is tessellation!!)";
378 return DrawResult::kFail;
379 }
380
381 canvas->clear(SK_ColorBLACK);
382 SkPaint borderPaint;
383 borderPaint.setColor4f({0,1,1,1});
384 borderPaint.setAntiAlias(true);
385 canvas->drawPath(build_outset_triangle(kTri1), borderPaint);
386 canvas->drawPath(build_outset_triangle(kTri2), borderPaint);
387
388 borderPaint.setColor4f({1,0,1,1});
389 canvas->drawRect(kRect.makeOutset(1.5f, 1.5f), borderPaint);
390
391 GrOpMemoryPool* pool = ctx->priv().opMemoryPool();
392 rtc->priv().testingOnly_addDrawOp(
393 pool->allocate<TessellationTestOp>(canvas->getTotalMatrix(), kTri1));
394 rtc->priv().testingOnly_addDrawOp(
395 pool->allocate<TessellationTestOp>(canvas->getTotalMatrix(), kTri2));
396 rtc->priv().testingOnly_addDrawOp(
397 pool->allocate<TessellationTestOp>(canvas->getTotalMatrix(), nullptr));
398
399 return skiagm::DrawResult::kOk;
400}
401
402DEF_GM( return new TessellationGM(); )
403
404}