Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 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 | |
Robert Phillips | 7a0d3c3 | 2021-07-21 15:39:51 -0400 | [diff] [blame] | 10 | #include "src/core/SkCanvasPriv.h" |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 11 | #include "src/gpu/GrCaps.h" |
Adlai Holler | a069304 | 2020-10-14 11:23:11 -0400 | [diff] [blame] | 12 | #include "src/gpu/GrDirectContextPriv.h" |
Robert Phillips | 787fd9d | 2021-03-22 14:48:09 -0400 | [diff] [blame] | 13 | #include "src/gpu/GrGeometryProcessor.h" |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 14 | #include "src/gpu/GrMemoryPool.h" |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 15 | #include "src/gpu/GrOpFlushState.h" |
| 16 | #include "src/gpu/GrOpsRenderPass.h" |
| 17 | #include "src/gpu/GrPipeline.h" |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 18 | #include "src/gpu/GrProgramInfo.h" |
| 19 | #include "src/gpu/GrRecordingContextPriv.h" |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 20 | #include "src/gpu/GrShaderCaps.h" |
| 21 | #include "src/gpu/GrShaderVar.h" |
| 22 | #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h" |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 23 | #include "src/gpu/glsl/GrGLSLVarying.h" |
| 24 | #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h" |
| 25 | #include "src/gpu/ops/GrDrawOp.h" |
Robert Phillips | 4dca831 | 2021-07-28 15:13:20 -0400 | [diff] [blame] | 26 | #include "src/gpu/v1/SurfaceDrawContext_v1.h" |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 27 | |
| 28 | namespace skiagm { |
| 29 | |
| 30 | constexpr static GrGeometryProcessor::Attribute kPositionAttrib = |
| 31 | {"position", kFloat3_GrVertexAttribType, kFloat3_GrSLType}; |
| 32 | |
| 33 | constexpr static std::array<float, 3> kTri1[3] = { |
| 34 | {20.5f,20.5f,1}, {170.5f,280.5f,4}, {320.5f,20.5f,1}}; |
| 35 | constexpr static std::array<float, 3> kTri2[3] = { |
| 36 | {640.5f,280.5f,3}, {490.5f,20.5f,1}, {340.5f,280.5f,6}}; |
| 37 | constexpr static SkRect kRect = {20.5f, 340.5f, 640.5f, 480.5f}; |
| 38 | |
| 39 | constexpr static int kWidth = (int)kRect.fRight + 21; |
| 40 | constexpr static int kHeight = (int)kRect.fBottom + 21; |
| 41 | |
| 42 | /** |
| 43 | * This is a GPU-backend specific test. It ensures that tessellation works as expected by drawing |
| 44 | * several triangles. The test passes as long as the triangle tessellations match the reference |
| 45 | * images on gold. |
| 46 | */ |
| 47 | class TessellationGM : public GpuGM { |
| 48 | SkString onShortName() override { return SkString("tessellation"); } |
| 49 | SkISize onISize() override { return {kWidth, kHeight}; } |
Robert Phillips | 7a0d3c3 | 2021-07-21 15:39:51 -0400 | [diff] [blame] | 50 | DrawResult onDraw(GrRecordingContext*, SkCanvas*, SkString*) override; |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 51 | }; |
| 52 | |
| 53 | |
| 54 | class TessellationTestTriShader : public GrGeometryProcessor { |
| 55 | public: |
| 56 | TessellationTestTriShader(const SkMatrix& viewMatrix) |
| 57 | : GrGeometryProcessor(kTessellationTestTriShader_ClassID), fViewMatrix(viewMatrix) { |
| 58 | this->setVertexAttributes(&kPositionAttrib, 1); |
| 59 | this->setWillUseTessellationShaders(); |
| 60 | } |
| 61 | |
Robert Phillips | 709e240 | 2020-03-23 18:29:16 +0000 | [diff] [blame] | 62 | private: |
| 63 | const char* name() const final { return "TessellationTestTriShader"; } |
Brian Salomon | 13b2873 | 2021-08-06 15:33:58 -0400 | [diff] [blame] | 64 | void addToKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {} |
Robert Phillips | 709e240 | 2020-03-23 18:29:16 +0000 | [diff] [blame] | 65 | |
Brian Salomon | f95940b | 2021-08-09 15:56:24 -0400 | [diff] [blame] | 66 | class Impl : public ProgramImpl { |
Brian Salomon | bab2d11 | 2021-08-11 09:59:56 -0400 | [diff] [blame] | 67 | public: |
| 68 | void setData(const GrGLSLProgramDataManager& pdman, |
| 69 | const GrShaderCaps&, |
| 70 | const GrGeometryProcessor& geomProc) override { |
| 71 | pdman.setSkMatrix(fViewMatrixUniform, |
| 72 | geomProc.cast<TessellationTestTriShader>().fViewMatrix); |
| 73 | } |
| 74 | |
| 75 | private: |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 76 | void onEmitCode(EmitArgs& args, GrGPArgs*) override { |
Robert Phillips | 787fd9d | 2021-03-22 14:48:09 -0400 | [diff] [blame] | 77 | args.fVaryingHandler->emitAttributes(args.fGeomProc.cast<TessellationTestTriShader>()); |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 78 | const char* viewMatrix; |
| 79 | fViewMatrixUniform = args.fUniformHandler->addUniform( |
Ethan Nicholas | 16464c3 | 2020-04-06 13:53:05 -0400 | [diff] [blame] | 80 | nullptr, kVertex_GrShaderFlag, kFloat3x3_GrSLType, "view_matrix", &viewMatrix); |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 81 | args.fVertBuilder->declareGlobal( |
Ben Wagner | dabd98c | 2020-03-25 15:17:18 -0400 | [diff] [blame] | 82 | GrShaderVar("P_", kFloat3_GrSLType, GrShaderVar::TypeModifier::Out)); |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 83 | args.fVertBuilder->codeAppendf(R"( |
Chris Dalton | 85dbeb5 | 2021-05-10 14:10:05 -0600 | [diff] [blame] | 84 | P_.xy = (%s * float3(position.xy, 1)).xy; |
| 85 | P_.z = position.z;)", viewMatrix); |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 86 | // GrGLProgramBuilder will call writeTess*ShaderGLSL when it is compiling. |
| 87 | this->writeFragmentShader(args.fFragBuilder, args.fOutputColor, args.fOutputCoverage); |
| 88 | } |
Brian Salomon | bab2d11 | 2021-08-11 09:59:56 -0400 | [diff] [blame] | 89 | |
Chris Dalton | 85dbeb5 | 2021-05-10 14:10:05 -0600 | [diff] [blame] | 90 | SkString getTessControlShaderGLSL(const GrGeometryProcessor&, |
| 91 | const char* versionAndExtensionDecls, |
| 92 | const GrGLSLUniformHandler&, |
| 93 | const GrShaderCaps&) const override { |
| 94 | SkString code(versionAndExtensionDecls); |
| 95 | code.append(R"( |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 96 | layout(vertices = 3) out; |
| 97 | |
| 98 | in vec3 P_[]; |
| 99 | out vec3 P[]; |
| 100 | |
| 101 | void main() { |
| 102 | P[gl_InvocationID] = P_[gl_InvocationID]; |
| 103 | gl_TessLevelOuter[gl_InvocationID] = P_[gl_InvocationID].z; |
| 104 | gl_TessLevelInner[0] = 2.0; |
| 105 | })"); |
| 106 | |
Chris Dalton | 85dbeb5 | 2021-05-10 14:10:05 -0600 | [diff] [blame] | 107 | return code; |
| 108 | } |
Brian Salomon | bab2d11 | 2021-08-11 09:59:56 -0400 | [diff] [blame] | 109 | |
Chris Dalton | 85dbeb5 | 2021-05-10 14:10:05 -0600 | [diff] [blame] | 110 | SkString getTessEvaluationShaderGLSL(const GrGeometryProcessor&, |
| 111 | const char* versionAndExtensionDecls, |
| 112 | const GrGLSLUniformHandler&, |
| 113 | const GrShaderCaps&) const override { |
| 114 | SkString code(versionAndExtensionDecls); |
| 115 | code.append(R"( |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 116 | layout(triangles, equal_spacing, cw) in; |
| 117 | |
| 118 | uniform vec4 sk_RTAdjust; |
| 119 | |
| 120 | in vec3 P[]; |
| 121 | out vec3 barycentric_coord; |
| 122 | |
| 123 | void main() { |
| 124 | vec2 devcoord = mat3x2(P[0].xy, P[1].xy, P[2].xy) * gl_TessCoord.xyz; |
| 125 | devcoord = round(devcoord - .5) + .5; // Make horz and vert lines on px bounds. |
| 126 | gl_Position = vec4(devcoord.xy * sk_RTAdjust.xz + sk_RTAdjust.yw, 0.0, 1.0); |
| 127 | |
| 128 | float i = 0.0; |
| 129 | if (gl_TessCoord.y == 0.0) { |
| 130 | i += gl_TessCoord.z * P[1].z; |
| 131 | } else { |
| 132 | i += P[1].z; |
| 133 | if (gl_TessCoord.x == 0.0) { |
| 134 | i += gl_TessCoord.y * P[0].z; |
| 135 | } else { |
| 136 | i += P[0].z; |
Chris Dalton | 8dae7eb | 2019-12-27 22:20:55 -0700 | [diff] [blame] | 137 | if (gl_TessCoord.z == 0.0) { |
| 138 | i += gl_TessCoord.x * P[2].z; |
| 139 | } else { |
| 140 | barycentric_coord = vec3(0, 1, 0); |
| 141 | return; |
| 142 | } |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 143 | } |
| 144 | } |
Chris Dalton | 8dae7eb | 2019-12-27 22:20:55 -0700 | [diff] [blame] | 145 | i = abs(mod(i, 2.0) - 1.0); |
| 146 | barycentric_coord = vec3(i, 0, 1.0 - i); |
| 147 | })"); |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 148 | |
Chris Dalton | 85dbeb5 | 2021-05-10 14:10:05 -0600 | [diff] [blame] | 149 | return code; |
| 150 | } |
Brian Salomon | bab2d11 | 2021-08-11 09:59:56 -0400 | [diff] [blame] | 151 | |
Chris Dalton | 85dbeb5 | 2021-05-10 14:10:05 -0600 | [diff] [blame] | 152 | void writeFragmentShader(GrGLSLFPFragmentBuilder* f, const char* color, |
| 153 | const char* coverage) { |
| 154 | f->declareGlobal(GrShaderVar("barycentric_coord", kFloat3_GrSLType, |
| 155 | GrShaderVar::TypeModifier::In)); |
| 156 | f->codeAppendf(R"( |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 157 | half3 d = half3(1 - barycentric_coord/fwidth(barycentric_coord)); |
| 158 | half coverage = max(max(d.x, d.y), d.z); |
John Stiles | 4d7ac49 | 2021-03-09 20:16:43 -0500 | [diff] [blame] | 159 | half4 %s = half4(0, coverage, coverage, 1); |
| 160 | const half4 %s = half4(1);)", color, coverage); |
Chris Dalton | 85dbeb5 | 2021-05-10 14:10:05 -0600 | [diff] [blame] | 161 | } |
Brian Salomon | bab2d11 | 2021-08-11 09:59:56 -0400 | [diff] [blame] | 162 | |
Chris Dalton | 85dbeb5 | 2021-05-10 14:10:05 -0600 | [diff] [blame] | 163 | GrGLSLUniformHandler::UniformHandle fViewMatrixUniform; |
| 164 | }; |
| 165 | |
Brian Salomon | f95940b | 2021-08-09 15:56:24 -0400 | [diff] [blame] | 166 | std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const final { |
| 167 | return std::make_unique<Impl>(); |
Chris Dalton | 85dbeb5 | 2021-05-10 14:10:05 -0600 | [diff] [blame] | 168 | } |
| 169 | |
| 170 | const SkMatrix fViewMatrix; |
| 171 | }; |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 172 | |
| 173 | class TessellationTestRectShader : public GrGeometryProcessor { |
| 174 | public: |
| 175 | TessellationTestRectShader(const SkMatrix& viewMatrix) |
| 176 | : GrGeometryProcessor(kTessellationTestTriShader_ClassID), fViewMatrix(viewMatrix) { |
| 177 | this->setWillUseTessellationShaders(); |
| 178 | } |
| 179 | |
Robert Phillips | 709e240 | 2020-03-23 18:29:16 +0000 | [diff] [blame] | 180 | const char* name() const final { return "TessellationTestRectShader"; } |
Brian Salomon | bab2d11 | 2021-08-11 09:59:56 -0400 | [diff] [blame] | 181 | |
Brian Salomon | 13b2873 | 2021-08-06 15:33:58 -0400 | [diff] [blame] | 182 | void addToKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {} |
Robert Phillips | 709e240 | 2020-03-23 18:29:16 +0000 | [diff] [blame] | 183 | |
Brian Salomon | bab2d11 | 2021-08-11 09:59:56 -0400 | [diff] [blame] | 184 | private: |
Brian Salomon | f95940b | 2021-08-09 15:56:24 -0400 | [diff] [blame] | 185 | class Impl : public ProgramImpl { |
Brian Salomon | bab2d11 | 2021-08-11 09:59:56 -0400 | [diff] [blame] | 186 | public: |
| 187 | void setData(const GrGLSLProgramDataManager& pdman, |
| 188 | const GrShaderCaps&, |
| 189 | const GrGeometryProcessor& geomProc) override { |
| 190 | pdman.setSkMatrix(fViewMatrixUniform, |
| 191 | geomProc.cast<TessellationTestRectShader>().fViewMatrix); |
| 192 | } |
| 193 | |
| 194 | private: |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 195 | void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { |
| 196 | const char* viewMatrix; |
| 197 | fViewMatrixUniform = args.fUniformHandler->addUniform( |
Ethan Nicholas | 16464c3 | 2020-04-06 13:53:05 -0400 | [diff] [blame] | 198 | nullptr, kVertex_GrShaderFlag, kFloat3x3_GrSLType, "view_matrix", &viewMatrix); |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 199 | args.fVertBuilder->declareGlobal( |
Ben Wagner | dabd98c | 2020-03-25 15:17:18 -0400 | [diff] [blame] | 200 | GrShaderVar("M_", kFloat3x3_GrSLType, GrShaderVar::TypeModifier::Out)); |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 201 | args.fVertBuilder->codeAppendf("M_ = %s;", viewMatrix); |
| 202 | // GrGLProgramBuilder will call writeTess*ShaderGLSL when it is compiling. |
| 203 | this->writeFragmentShader(args.fFragBuilder, args.fOutputColor, args.fOutputCoverage); |
| 204 | } |
Brian Salomon | bab2d11 | 2021-08-11 09:59:56 -0400 | [diff] [blame] | 205 | |
Chris Dalton | 85dbeb5 | 2021-05-10 14:10:05 -0600 | [diff] [blame] | 206 | SkString getTessControlShaderGLSL(const GrGeometryProcessor&, |
| 207 | const char* versionAndExtensionDecls, |
| 208 | const GrGLSLUniformHandler&, |
| 209 | const GrShaderCaps&) const override { |
| 210 | SkString code(versionAndExtensionDecls); |
| 211 | code.append(R"( |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 212 | layout(vertices = 1) out; |
| 213 | |
| 214 | in mat3 M_[]; |
| 215 | out mat3 M[]; |
| 216 | |
| 217 | void main() { |
| 218 | M[gl_InvocationID] = M_[gl_InvocationID]; |
| 219 | gl_TessLevelInner[0] = 8.0; |
| 220 | gl_TessLevelInner[1] = 2.0; |
| 221 | gl_TessLevelOuter[0] = 2.0; |
| 222 | gl_TessLevelOuter[1] = 8.0; |
| 223 | gl_TessLevelOuter[2] = 2.0; |
| 224 | gl_TessLevelOuter[3] = 8.0; |
| 225 | })"); |
| 226 | |
Chris Dalton | 85dbeb5 | 2021-05-10 14:10:05 -0600 | [diff] [blame] | 227 | return code; |
| 228 | } |
Brian Salomon | bab2d11 | 2021-08-11 09:59:56 -0400 | [diff] [blame] | 229 | |
Chris Dalton | 85dbeb5 | 2021-05-10 14:10:05 -0600 | [diff] [blame] | 230 | SkString getTessEvaluationShaderGLSL(const GrGeometryProcessor&, |
| 231 | const char* versionAndExtensionDecls, |
| 232 | const GrGLSLUniformHandler&, |
| 233 | const GrShaderCaps&) const override { |
| 234 | SkString code(versionAndExtensionDecls); |
| 235 | code.appendf(R"( |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 236 | layout(quads, equal_spacing, cw) in; |
| 237 | |
| 238 | uniform vec4 sk_RTAdjust; |
| 239 | |
| 240 | in mat3 M[]; |
| 241 | out vec4 barycentric_coord; |
| 242 | |
| 243 | void main() { |
| 244 | vec4 R = vec4(%f, %f, %f, %f); |
| 245 | vec2 localcoord = mix(R.xy, R.zw, gl_TessCoord.xy); |
| 246 | vec2 devcoord = (M[0] * vec3(localcoord, 1)).xy; |
| 247 | devcoord = round(devcoord - .5) + .5; // Make horz and vert lines on px bounds. |
| 248 | gl_Position = vec4(devcoord.xy * sk_RTAdjust.xz + sk_RTAdjust.yw, 0.0, 1.0); |
| 249 | |
| 250 | float i = gl_TessCoord.x * 8.0; |
| 251 | i = abs(mod(i, 2.0) - 1.0); |
| 252 | if (gl_TessCoord.y == 0.0 || gl_TessCoord.y == 1.0) { |
| 253 | barycentric_coord = vec4(i, 1.0 - i, 0, 0); |
| 254 | } else { |
| 255 | barycentric_coord = vec4(0, 0, i, 1.0 - i); |
| 256 | } |
| 257 | })", kRect.left(), kRect.top(), kRect.right(), kRect.bottom()); |
| 258 | |
Chris Dalton | 85dbeb5 | 2021-05-10 14:10:05 -0600 | [diff] [blame] | 259 | return code; |
| 260 | } |
Brian Salomon | bab2d11 | 2021-08-11 09:59:56 -0400 | [diff] [blame] | 261 | |
Chris Dalton | 85dbeb5 | 2021-05-10 14:10:05 -0600 | [diff] [blame] | 262 | void writeFragmentShader(GrGLSLFPFragmentBuilder* f, const char* color, |
| 263 | const char* coverage) { |
| 264 | f->declareGlobal(GrShaderVar("barycentric_coord", kFloat4_GrSLType, |
| 265 | GrShaderVar::TypeModifier::In)); |
| 266 | f->codeAppendf(R"( |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 267 | float4 fwidths = fwidth(barycentric_coord); |
| 268 | half coverage = 0; |
| 269 | for (int i = 0; i < 4; ++i) { |
| 270 | if (fwidths[i] != 0) { |
| 271 | coverage = half(max(coverage, 1 - barycentric_coord[i]/fwidths[i])); |
| 272 | } |
| 273 | } |
John Stiles | 9c4cdef | 2021-03-10 12:01:10 -0500 | [diff] [blame] | 274 | half4 %s = half4(coverage, 0, coverage, 1); |
| 275 | const half4 %s = half4(1);)", color, coverage); |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 276 | |
Chris Dalton | 85dbeb5 | 2021-05-10 14:10:05 -0600 | [diff] [blame] | 277 | } |
Brian Salomon | bab2d11 | 2021-08-11 09:59:56 -0400 | [diff] [blame] | 278 | |
Chris Dalton | 85dbeb5 | 2021-05-10 14:10:05 -0600 | [diff] [blame] | 279 | GrGLSLUniformHandler::UniformHandle fViewMatrixUniform; |
| 280 | }; |
| 281 | |
Brian Salomon | f95940b | 2021-08-09 15:56:24 -0400 | [diff] [blame] | 282 | std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const final { |
| 283 | return std::make_unique<Impl>(); |
Chris Dalton | 85dbeb5 | 2021-05-10 14:10:05 -0600 | [diff] [blame] | 284 | } |
| 285 | |
| 286 | const SkMatrix fViewMatrix; |
| 287 | }; |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 288 | |
| 289 | class TessellationTestOp : public GrDrawOp { |
| 290 | DEFINE_OP_CLASS_ID |
| 291 | |
| 292 | public: |
| 293 | TessellationTestOp(const SkMatrix& viewMatrix, const std::array<float, 3>* triPositions) |
Robert Phillips | 709e240 | 2020-03-23 18:29:16 +0000 | [diff] [blame] | 294 | : GrDrawOp(ClassID()), fViewMatrix(viewMatrix), fTriPositions(triPositions) { |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 295 | this->setBounds(SkRect::MakeIWH(kWidth, kHeight), HasAABloat::kNo, IsHairline::kNo); |
| 296 | } |
| 297 | |
| 298 | private: |
| 299 | const char* name() const override { return "TessellationTestOp"; } |
| 300 | FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; } |
Chris Dalton | 57ab06c | 2021-04-22 12:57:28 -0600 | [diff] [blame] | 301 | GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, GrClampType) override { |
Robert Phillips | 709e240 | 2020-03-23 18:29:16 +0000 | [diff] [blame] | 302 | return GrProcessorSet::EmptySetAnalysis(); |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 303 | } |
| 304 | |
Robert Phillips | 709e240 | 2020-03-23 18:29:16 +0000 | [diff] [blame] | 305 | void onPrePrepare(GrRecordingContext*, |
Adlai Holler | e2296f7 | 2020-11-19 13:41:26 -0500 | [diff] [blame] | 306 | const GrSurfaceProxyView& writeView, |
Robert Phillips | 709e240 | 2020-03-23 18:29:16 +0000 | [diff] [blame] | 307 | GrAppliedClip*, |
John Stiles | 52cb1d0 | 2021-06-02 11:58:05 -0400 | [diff] [blame] | 308 | const GrDstProxyView&, |
Greg Daniel | 42dbca5 | 2020-11-20 10:22:43 -0500 | [diff] [blame] | 309 | GrXferBarrierFlags renderPassXferBarriers, |
| 310 | GrLoadOp colorLoadOp) override {} |
Robert Phillips | c655c3a | 2020-03-18 13:23:45 -0400 | [diff] [blame] | 311 | |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 312 | void onPrepare(GrOpFlushState* flushState) override { |
| 313 | if (fTriPositions) { |
| 314 | if (void* vertexData = flushState->makeVertexSpace(sizeof(float) * 3, 3, &fVertexBuffer, |
| 315 | &fBaseVertex)) { |
| 316 | memcpy(vertexData, fTriPositions, sizeof(float) * 3 * 3); |
| 317 | } |
| 318 | } |
| 319 | } |
| 320 | |
| 321 | void onExecute(GrOpFlushState* state, const SkRect& chainBounds) override { |
Robert Phillips | 709e240 | 2020-03-23 18:29:16 +0000 | [diff] [blame] | 322 | GrPipeline pipeline(GrScissorTest::kDisabled, SkBlendMode::kSrc, |
Adlai Holler | e2296f7 | 2020-11-19 13:41:26 -0500 | [diff] [blame] | 323 | state->drawOpArgs().writeView().swizzle()); |
Robert Phillips | 709e240 | 2020-03-23 18:29:16 +0000 | [diff] [blame] | 324 | int tessellationPatchVertexCount; |
| 325 | std::unique_ptr<GrGeometryProcessor> shader; |
| 326 | if (fTriPositions) { |
| 327 | if (!fVertexBuffer) { |
| 328 | return; |
| 329 | } |
| 330 | tessellationPatchVertexCount = 3; |
| 331 | shader = std::make_unique<TessellationTestTriShader>(fViewMatrix); |
| 332 | } else { |
| 333 | // Use a mismatched number of vertices in the input patch vs output. |
| 334 | // (The tessellation control shader will output one vertex per patch.) |
| 335 | tessellationPatchVertexCount = 5; |
| 336 | shader = std::make_unique<TessellationTestRectShader>(fViewMatrix); |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 337 | } |
| 338 | |
Chris Dalton | 2a26c50 | 2021-08-26 10:05:11 -0600 | [diff] [blame^] | 339 | GrProgramInfo programInfo(state->caps(), state->writeView(), state->usesMSAASurface(), |
| 340 | &pipeline, &GrUserStencilSettings::kUnused, |
Robert Phillips | 5c80964 | 2020-11-20 12:28:45 -0500 | [diff] [blame] | 341 | shader.get(), GrPrimitiveType::kPatches, |
| 342 | tessellationPatchVertexCount, state->renderPassBarriers(), |
| 343 | state->colorLoadOp()); |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 344 | |
Robert Phillips | 709e240 | 2020-03-23 18:29:16 +0000 | [diff] [blame] | 345 | state->bindPipeline(programInfo, SkRect::MakeIWH(kWidth, kHeight)); |
Greg Daniel | 426274b | 2020-07-20 11:37:38 -0400 | [diff] [blame] | 346 | state->bindBuffers(nullptr, nullptr, std::move(fVertexBuffer)); |
Robert Phillips | 709e240 | 2020-03-23 18:29:16 +0000 | [diff] [blame] | 347 | state->draw(tessellationPatchVertexCount, fBaseVertex); |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 348 | } |
| 349 | |
| 350 | const SkMatrix fViewMatrix; |
| 351 | const std::array<float, 3>* const fTriPositions; |
| 352 | sk_sp<const GrBuffer> fVertexBuffer; |
| 353 | int fBaseVertex = 0; |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 354 | }; |
| 355 | |
| 356 | |
| 357 | static SkPath build_outset_triangle(const std::array<float, 3>* tri) { |
| 358 | SkPath outset; |
| 359 | for (int i = 0; i < 3; ++i) { |
| 360 | SkPoint p = {tri[i][0], tri[i][1]}; |
| 361 | SkPoint left = {tri[(i + 2) % 3][0], tri[(i + 2) % 3][1]}; |
| 362 | SkPoint right = {tri[(i + 1) % 3][0], tri[(i + 1) % 3][1]}; |
| 363 | SkPoint n0, n1; |
| 364 | n0.setNormalize(left.y() - p.y(), p.x() - left.x()); |
| 365 | n1.setNormalize(p.y() - right.y(), right.x() - p.x()); |
| 366 | p += (n0 + n1) * 3; |
| 367 | if (0 == i) { |
| 368 | outset.moveTo(p); |
| 369 | } else { |
| 370 | outset.lineTo(p); |
| 371 | } |
| 372 | } |
| 373 | return outset; |
| 374 | } |
| 375 | |
Robert Phillips | 7a0d3c3 | 2021-07-21 15:39:51 -0400 | [diff] [blame] | 376 | DrawResult TessellationGM::onDraw(GrRecordingContext* rContext, |
| 377 | SkCanvas* canvas, |
| 378 | SkString* errorMsg) { |
| 379 | auto sdc = SkCanvasPriv::TopDeviceSurfaceDrawContext(canvas); |
| 380 | if (!sdc) { |
| 381 | *errorMsg = kErrorMsg_DrawSkippedGpuOnly; |
| 382 | return DrawResult::kSkip; |
| 383 | } |
| 384 | |
Robert Phillips | edcd431 | 2021-06-03 10:14:16 -0400 | [diff] [blame] | 385 | if (!rContext->priv().caps()->shaderCaps()->tessellationSupport()) { |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 386 | *errorMsg = "Requires GPU tessellation support."; |
| 387 | return DrawResult::kSkip; |
| 388 | } |
Robert Phillips | edcd431 | 2021-06-03 10:14:16 -0400 | [diff] [blame] | 389 | if (!rContext->priv().caps()->shaderCaps()->shaderDerivativeSupport()) { |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 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 | |
Robert Phillips | edcd431 | 2021-06-03 10:14:16 -0400 | [diff] [blame] | 405 | sdc->addDrawOp(GrOp::Make<TessellationTestOp>(rContext, canvas->getTotalMatrix(), kTri1)); |
| 406 | sdc->addDrawOp(GrOp::Make<TessellationTestOp>(rContext, canvas->getTotalMatrix(), kTri2)); |
| 407 | sdc->addDrawOp(GrOp::Make<TessellationTestOp>(rContext, canvas->getTotalMatrix(), nullptr)); |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 408 | |
| 409 | return skiagm::DrawResult::kOk; |
| 410 | } |
| 411 | |
| 412 | DEF_GM( return new TessellationGM(); ) |
| 413 | |
John Stiles | a6841be | 2020-08-06 14:11:56 -0400 | [diff] [blame] | 414 | } // namespace skiagm |