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