1 /*
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/GrDirectContextPriv.h"
12 #include "src/gpu/GrMemoryPool.h"
13 #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
30 namespace skiagm {
31
32 constexpr static GrGeometryProcessor::Attribute kPositionAttrib =
33 {"position", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
34
35 constexpr static std::array<float, 3> kTri1[3] = {
36 {20.5f,20.5f,1}, {170.5f,280.5f,4}, {320.5f,20.5f,1}};
37 constexpr static std::array<float, 3> kTri2[3] = {
38 {640.5f,280.5f,3}, {490.5f,20.5f,1}, {340.5f,280.5f,6}};
39 constexpr static SkRect kRect = {20.5f, 340.5f, 640.5f, 480.5f};
40
41 constexpr static int kWidth = (int)kRect.fRight + 21;
42 constexpr 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 */
49 class TessellationGM : public GpuGM {
onShortName()50 SkString onShortName() override { return SkString("tessellation"); }
onISize()51 SkISize onISize() override { return {kWidth, kHeight}; }
52 DrawResult onDraw(GrRecordingContext*, GrRenderTargetContext*, SkCanvas*, SkString*) override;
53 };
54
55
56 class TessellationTestTriShader : public GrGeometryProcessor {
57 public:
TessellationTestTriShader(const SkMatrix & viewMatrix)58 TessellationTestTriShader(const SkMatrix& viewMatrix)
59 : GrGeometryProcessor(kTessellationTestTriShader_ClassID), fViewMatrix(viewMatrix) {
60 this->setVertexAttributes(&kPositionAttrib, 1);
61 this->setWillUseTessellationShaders();
62 }
63
64 private:
name() const65 const char* name() const final { return "TessellationTestTriShader"; }
getGLSLProcessorKey(const GrShaderCaps &,GrProcessorKeyBuilder * b) const66 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {}
67
68 class Impl : public GrGLSLGeometryProcessor {
onEmitCode(EmitArgs & args,GrGPArgs *)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 nullptr, kVertex_GrShaderFlag, kFloat3x3_GrSLType, "view_matrix", &viewMatrix);
74 args.fVertBuilder->declareGlobal(
75 GrShaderVar("P_", kFloat3_GrSLType, GrShaderVar::TypeModifier::Out));
76 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);
setData(const GrGLSLProgramDataManager & pdman,const GrPrimitiveProcessor & proc)83 void setData(const GrGLSLProgramDataManager& pdman,
84 const GrPrimitiveProcessor& proc) override {
85 pdman.setSkMatrix(fViewMatrixUniform,
86 proc.cast<TessellationTestTriShader>().fViewMatrix);
87 }
88 GrGLSLUniformHandler::UniformHandle fViewMatrixUniform;
89 };
90
createGLSLInstance(const GrShaderCaps &) const91 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
92 return new Impl;
93 }
94
95 SkString getTessControlShaderGLSL(const GrGLSLPrimitiveProcessor*,
96 const char* versionAndExtensionDecls,
97 const GrGLSLUniformHandler&,
98 const GrShaderCaps&) const override;
99 SkString getTessEvaluationShaderGLSL(const GrGLSLPrimitiveProcessor*,
100 const char* versionAndExtensionDecls,
101 const GrGLSLUniformHandler&,
102 const GrShaderCaps&) const override;
103
104 const SkMatrix fViewMatrix;
105 };
106
getTessControlShaderGLSL(const GrGLSLPrimitiveProcessor *,const char * versionAndExtensionDecls,const GrGLSLUniformHandler &,const GrShaderCaps &) const107 SkString TessellationTestTriShader::getTessControlShaderGLSL(
108 const GrGLSLPrimitiveProcessor*, const char* versionAndExtensionDecls,
109 const GrGLSLUniformHandler&, const GrShaderCaps&) const {
110 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
getTessEvaluationShaderGLSL(const GrGLSLPrimitiveProcessor *,const char * versionAndExtensionDecls,const GrGLSLUniformHandler &,const GrShaderCaps &) const126 SkString TessellationTestTriShader::getTessEvaluationShaderGLSL(
127 const GrGLSLPrimitiveProcessor*, const char* versionAndExtensionDecls,
128 const GrGLSLUniformHandler&, const GrShaderCaps&) const {
129 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;
152 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 }
158 }
159 }
160 i = abs(mod(i, 2.0) - 1.0);
161 barycentric_coord = vec3(i, 0, 1.0 - i);
162 })");
163
164 return code;
165 }
166
writeFragmentShader(GrGLSLFPFragmentBuilder * f,const char * color,const char * coverage)167 void TessellationTestTriShader::Impl::writeFragmentShader(
168 GrGLSLFPFragmentBuilder* f, const char* color, const char* coverage) {
169 f->declareGlobal(
170 GrShaderVar("barycentric_coord", kFloat3_GrSLType, GrShaderVar::TypeModifier::In));
171 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
178 class TessellationTestRectShader : public GrGeometryProcessor {
179 public:
TessellationTestRectShader(const SkMatrix & viewMatrix)180 TessellationTestRectShader(const SkMatrix& viewMatrix)
181 : GrGeometryProcessor(kTessellationTestTriShader_ClassID), fViewMatrix(viewMatrix) {
182 this->setWillUseTessellationShaders();
183 }
184
185 private:
name() const186 const char* name() const final { return "TessellationTestRectShader"; }
getGLSLProcessorKey(const GrShaderCaps &,GrProcessorKeyBuilder * b) const187 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {}
188
189 class Impl : public GrGLSLGeometryProcessor {
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)190 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
191 const char* viewMatrix;
192 fViewMatrixUniform = args.fUniformHandler->addUniform(
193 nullptr, kVertex_GrShaderFlag, kFloat3x3_GrSLType, "view_matrix", &viewMatrix);
194 args.fVertBuilder->declareGlobal(
195 GrShaderVar("M_", kFloat3x3_GrSLType, GrShaderVar::TypeModifier::Out));
196 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);
setData(const GrGLSLProgramDataManager & pdman,const GrPrimitiveProcessor & proc)201 void setData(const GrGLSLProgramDataManager& pdman,
202 const GrPrimitiveProcessor& proc) override {
203 pdman.setSkMatrix(fViewMatrixUniform,
204 proc.cast<TessellationTestRectShader>().fViewMatrix);
205 }
206 GrGLSLUniformHandler::UniformHandle fViewMatrixUniform;
207 };
208
createGLSLInstance(const GrShaderCaps &) const209 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
210 return new Impl;
211 }
212
213 SkString getTessControlShaderGLSL(const GrGLSLPrimitiveProcessor*,
214 const char* versionAndExtensionDecls,
215 const GrGLSLUniformHandler&,
216 const GrShaderCaps&) const override;
217 SkString getTessEvaluationShaderGLSL(const GrGLSLPrimitiveProcessor*,
218 const char* versionAndExtensionDecls,
219 const GrGLSLUniformHandler&,
220 const GrShaderCaps&) const override;
221
222 const SkMatrix fViewMatrix;
223 };
224
getTessControlShaderGLSL(const GrGLSLPrimitiveProcessor *,const char * versionAndExtensionDecls,const GrGLSLUniformHandler &,const GrShaderCaps & caps) const225 SkString TessellationTestRectShader::getTessControlShaderGLSL(
226 const GrGLSLPrimitiveProcessor*, const char* versionAndExtensionDecls,
227 const GrGLSLUniformHandler&, const GrShaderCaps& caps) const {
228 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
getTessEvaluationShaderGLSL(const GrGLSLPrimitiveProcessor *,const char * versionAndExtensionDecls,const GrGLSLUniformHandler &,const GrShaderCaps & caps) const248 SkString TessellationTestRectShader::getTessEvaluationShaderGLSL(
249 const GrGLSLPrimitiveProcessor*, const char* versionAndExtensionDecls,
250 const GrGLSLUniformHandler&, const GrShaderCaps& caps) const {
251 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
writeFragmentShader(GrGLSLFPFragmentBuilder * f,const char * color,const char * coverage)279 void TessellationTestRectShader::Impl::writeFragmentShader(
280 GrGLSLFPFragmentBuilder* f, const char* color, const char* coverage) {
281 f->declareGlobal(GrShaderVar("barycentric_coord", kFloat4_GrSLType,
282 GrShaderVar::TypeModifier::In));
283 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
296 class TessellationTestOp : public GrDrawOp {
297 DEFINE_OP_CLASS_ID
298
299 public:
TessellationTestOp(const SkMatrix & viewMatrix,const std::array<float,3> * triPositions)300 TessellationTestOp(const SkMatrix& viewMatrix, const std::array<float, 3>* triPositions)
301 : GrDrawOp(ClassID()), fViewMatrix(viewMatrix), fTriPositions(triPositions) {
302 this->setBounds(SkRect::MakeIWH(kWidth, kHeight), HasAABloat::kNo, IsHairline::kNo);
303 }
304
305 private:
name() const306 const char* name() const override { return "TessellationTestOp"; }
fixedFunctionFlags() const307 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
finalize(const GrCaps &,const GrAppliedClip *,bool hasMixedSampledCoverage,GrClampType)308 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*,
309 bool hasMixedSampledCoverage, GrClampType) override {
310 return GrProcessorSet::EmptySetAnalysis();
311 }
312
onPrePrepare(GrRecordingContext *,const GrSurfaceProxyView * writeView,GrAppliedClip *,const GrXferProcessor::DstProxyView &,GrXferBarrierFlags renderPassXferBarriers)313 void onPrePrepare(GrRecordingContext*,
314 const GrSurfaceProxyView* writeView,
315 GrAppliedClip*,
316 const GrXferProcessor::DstProxyView&,
317 GrXferBarrierFlags renderPassXferBarriers) override {}
318
onPrepare(GrOpFlushState * flushState)319 void onPrepare(GrOpFlushState* flushState) override {
320 if (fTriPositions) {
321 if (void* vertexData = flushState->makeVertexSpace(sizeof(float) * 3, 3, &fVertexBuffer,
322 &fBaseVertex)) {
323 memcpy(vertexData, fTriPositions, sizeof(float) * 3 * 3);
324 }
325 }
326 }
327
onExecute(GrOpFlushState * state,const SkRect & chainBounds)328 void onExecute(GrOpFlushState* state, const SkRect& chainBounds) override {
329 GrPipeline pipeline(GrScissorTest::kDisabled, SkBlendMode::kSrc,
330 state->drawOpArgs().writeSwizzle());
331 int tessellationPatchVertexCount;
332 std::unique_ptr<GrGeometryProcessor> shader;
333 if (fTriPositions) {
334 if (!fVertexBuffer) {
335 return;
336 }
337 tessellationPatchVertexCount = 3;
338 shader = std::make_unique<TessellationTestTriShader>(fViewMatrix);
339 } else {
340 // Use a mismatched number of vertices in the input patch vs output.
341 // (The tessellation control shader will output one vertex per patch.)
342 tessellationPatchVertexCount = 5;
343 shader = std::make_unique<TessellationTestRectShader>(fViewMatrix);
344 }
345
346 GrProgramInfo programInfo(state->proxy()->numSamples(), state->proxy()->numStencilSamples(),
347 state->proxy()->backendFormat(), state->writeView()->origin(),
348 &pipeline, &GrUserStencilSettings::kUnused, shader.get(),
349 GrPrimitiveType::kPatches, tessellationPatchVertexCount,
350 state->renderPassBarriers());
351
352 state->bindPipeline(programInfo, SkRect::MakeIWH(kWidth, kHeight));
353 state->bindBuffers(nullptr, nullptr, std::move(fVertexBuffer));
354 state->draw(tessellationPatchVertexCount, fBaseVertex);
355 }
356
357 const SkMatrix fViewMatrix;
358 const std::array<float, 3>* const fTriPositions;
359 sk_sp<const GrBuffer> fVertexBuffer;
360 int fBaseVertex = 0;
361 };
362
363
build_outset_triangle(const std::array<float,3> * tri)364 static SkPath build_outset_triangle(const std::array<float, 3>* tri) {
365 SkPath outset;
366 for (int i = 0; i < 3; ++i) {
367 SkPoint p = {tri[i][0], tri[i][1]};
368 SkPoint left = {tri[(i + 2) % 3][0], tri[(i + 2) % 3][1]};
369 SkPoint right = {tri[(i + 1) % 3][0], tri[(i + 1) % 3][1]};
370 SkPoint n0, n1;
371 n0.setNormalize(left.y() - p.y(), p.x() - left.x());
372 n1.setNormalize(p.y() - right.y(), right.x() - p.x());
373 p += (n0 + n1) * 3;
374 if (0 == i) {
375 outset.moveTo(p);
376 } else {
377 outset.lineTo(p);
378 }
379 }
380 return outset;
381 }
382
onDraw(GrRecordingContext * ctx,GrRenderTargetContext * rtc,SkCanvas * canvas,SkString * errorMsg)383 DrawResult TessellationGM::onDraw(GrRecordingContext* ctx, GrRenderTargetContext* rtc,
384 SkCanvas* canvas, SkString* errorMsg) {
385 if (!ctx->priv().caps()->shaderCaps()->tessellationSupport()) {
386 *errorMsg = "Requires GPU tessellation support.";
387 return DrawResult::kSkip;
388 }
389 if (!ctx->priv().caps()->shaderCaps()->shaderDerivativeSupport()) {
390 *errorMsg = "Requires shader derivatives."
391 "(These are expected to always be present when there is tessellation!!)";
392 return DrawResult::kFail;
393 }
394
395 canvas->clear(SK_ColorBLACK);
396 SkPaint borderPaint;
397 borderPaint.setColor4f({0,1,1,1});
398 borderPaint.setAntiAlias(true);
399 canvas->drawPath(build_outset_triangle(kTri1), borderPaint);
400 canvas->drawPath(build_outset_triangle(kTri2), borderPaint);
401
402 borderPaint.setColor4f({1,0,1,1});
403 canvas->drawRect(kRect.makeOutset(1.5f, 1.5f), borderPaint);
404
405 rtc->priv().testingOnly_addDrawOp(
406 GrOp::Make<TessellationTestOp>(ctx, canvas->getTotalMatrix(), kTri1));
407 rtc->priv().testingOnly_addDrawOp(
408 GrOp::Make<TessellationTestOp>(ctx, canvas->getTotalMatrix(), kTri2));
409 rtc->priv().testingOnly_addDrawOp(
410 GrOp::Make<TessellationTestOp>(ctx, canvas->getTotalMatrix(), nullptr));
411
412 return skiagm::DrawResult::kOk;
413 }
414
415 DEF_GM( return new TessellationGM(); )
416
417 } // namespace skiagm
418