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" |
| 11 | #include "src/gpu/GrContextPriv.h" |
| 12 | #include "src/gpu/GrMemoryPool.h" |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 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 { |
| 50 | SkString onShortName() override { return SkString("tessellation"); } |
| 51 | SkISize onISize() override { return {kWidth, kHeight}; } |
Robert Phillips | 95c250c | 2020-06-29 15:36:12 -0400 | [diff] [blame] | 52 | DrawResult onDraw(GrRecordingContext*, GrRenderTargetContext*, SkCanvas*, SkString*) override; |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 53 | }; |
| 54 | |
| 55 | |
| 56 | class TessellationTestTriShader : public GrGeometryProcessor { |
| 57 | public: |
| 58 | TessellationTestTriShader(const SkMatrix& viewMatrix) |
| 59 | : GrGeometryProcessor(kTessellationTestTriShader_ClassID), fViewMatrix(viewMatrix) { |
| 60 | this->setVertexAttributes(&kPositionAttrib, 1); |
| 61 | this->setWillUseTessellationShaders(); |
| 62 | } |
| 63 | |
Robert Phillips | 709e240 | 2020-03-23 18:29:16 +0000 | [diff] [blame] | 64 | private: |
| 65 | const char* name() const final { return "TessellationTestTriShader"; } |
| 66 | void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {} |
| 67 | |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 68 | 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 Nicholas | 16464c3 | 2020-04-06 13:53:05 -0400 | [diff] [blame] | 73 | nullptr, kVertex_GrShaderFlag, kFloat3x3_GrSLType, "view_matrix", &viewMatrix); |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 74 | args.fVertBuilder->declareGlobal( |
Ben Wagner | dabd98c | 2020-03-25 15:17:18 -0400 | [diff] [blame] | 75 | GrShaderVar("P_", kFloat3_GrSLType, GrShaderVar::TypeModifier::Out)); |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 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); |
Brian Osman | 609f159 | 2020-07-01 15:14:39 -0400 | [diff] [blame^] | 83 | void setData(const GrGLSLProgramDataManager& pdman, |
| 84 | const GrPrimitiveProcessor& proc) override { |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 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 Dalton | 54c9093 | 2020-06-24 10:51:52 -0600 | [diff] [blame] | 95 | SkString getTessControlShaderGLSL(const GrGLSLPrimitiveProcessor*, |
| 96 | const char* versionAndExtensionDecls, |
| 97 | const GrGLSLUniformHandler&, |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 98 | const GrShaderCaps&) const override; |
Chris Dalton | 54c9093 | 2020-06-24 10:51:52 -0600 | [diff] [blame] | 99 | SkString getTessEvaluationShaderGLSL(const GrGLSLPrimitiveProcessor*, |
| 100 | const char* versionAndExtensionDecls, |
| 101 | const GrGLSLUniformHandler&, |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 102 | const GrShaderCaps&) const override; |
| 103 | |
| 104 | const SkMatrix fViewMatrix; |
| 105 | }; |
| 106 | |
| 107 | SkString TessellationTestTriShader::getTessControlShaderGLSL( |
Chris Dalton | 54c9093 | 2020-06-24 10:51:52 -0600 | [diff] [blame] | 108 | const GrGLSLPrimitiveProcessor*, const char* versionAndExtensionDecls, |
| 109 | const GrGLSLUniformHandler&, const GrShaderCaps&) const { |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 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 | |
| 126 | SkString TessellationTestTriShader::getTessEvaluationShaderGLSL( |
Chris Dalton | 54c9093 | 2020-06-24 10:51:52 -0600 | [diff] [blame] | 127 | const GrGLSLPrimitiveProcessor*, const char* versionAndExtensionDecls, |
| 128 | const GrGLSLUniformHandler&, const GrShaderCaps&) const { |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 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; |
Chris Dalton | 8dae7eb | 2019-12-27 22:20:55 -0700 | [diff] [blame] | 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 | } |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 158 | } |
| 159 | } |
Chris Dalton | 8dae7eb | 2019-12-27 22:20:55 -0700 | [diff] [blame] | 160 | i = abs(mod(i, 2.0) - 1.0); |
| 161 | barycentric_coord = vec3(i, 0, 1.0 - i); |
| 162 | })"); |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 163 | |
| 164 | return code; |
| 165 | } |
| 166 | |
| 167 | void TessellationTestTriShader::Impl::writeFragmentShader( |
| 168 | GrGLSLFPFragmentBuilder* f, const char* color, const char* coverage) { |
| 169 | f->declareGlobal( |
Ben Wagner | dabd98c | 2020-03-25 15:17:18 -0400 | [diff] [blame] | 170 | GrShaderVar("barycentric_coord", kFloat3_GrSLType, GrShaderVar::TypeModifier::In)); |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 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: |
| 180 | TessellationTestRectShader(const SkMatrix& viewMatrix) |
| 181 | : GrGeometryProcessor(kTessellationTestTriShader_ClassID), fViewMatrix(viewMatrix) { |
| 182 | this->setWillUseTessellationShaders(); |
| 183 | } |
| 184 | |
Robert Phillips | 709e240 | 2020-03-23 18:29:16 +0000 | [diff] [blame] | 185 | private: |
| 186 | const char* name() const final { return "TessellationTestRectShader"; } |
| 187 | void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {} |
| 188 | |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 189 | class Impl : public GrGLSLGeometryProcessor { |
| 190 | void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { |
| 191 | const char* viewMatrix; |
| 192 | fViewMatrixUniform = args.fUniformHandler->addUniform( |
Ethan Nicholas | 16464c3 | 2020-04-06 13:53:05 -0400 | [diff] [blame] | 193 | nullptr, kVertex_GrShaderFlag, kFloat3x3_GrSLType, "view_matrix", &viewMatrix); |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 194 | args.fVertBuilder->declareGlobal( |
Ben Wagner | dabd98c | 2020-03-25 15:17:18 -0400 | [diff] [blame] | 195 | GrShaderVar("M_", kFloat3x3_GrSLType, GrShaderVar::TypeModifier::Out)); |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 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); |
Brian Osman | 609f159 | 2020-07-01 15:14:39 -0400 | [diff] [blame^] | 201 | void setData(const GrGLSLProgramDataManager& pdman, |
| 202 | const GrPrimitiveProcessor& proc) override { |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 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 Dalton | 54c9093 | 2020-06-24 10:51:52 -0600 | [diff] [blame] | 213 | SkString getTessControlShaderGLSL(const GrGLSLPrimitiveProcessor*, |
| 214 | const char* versionAndExtensionDecls, |
| 215 | const GrGLSLUniformHandler&, |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 216 | const GrShaderCaps&) const override; |
Chris Dalton | 54c9093 | 2020-06-24 10:51:52 -0600 | [diff] [blame] | 217 | SkString getTessEvaluationShaderGLSL(const GrGLSLPrimitiveProcessor*, |
| 218 | const char* versionAndExtensionDecls, |
| 219 | const GrGLSLUniformHandler&, |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 220 | const GrShaderCaps&) const override; |
| 221 | |
| 222 | const SkMatrix fViewMatrix; |
| 223 | }; |
| 224 | |
| 225 | SkString TessellationTestRectShader::getTessControlShaderGLSL( |
Chris Dalton | 54c9093 | 2020-06-24 10:51:52 -0600 | [diff] [blame] | 226 | const GrGLSLPrimitiveProcessor*, const char* versionAndExtensionDecls, |
| 227 | const GrGLSLUniformHandler&, const GrShaderCaps& caps) const { |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 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 | |
| 248 | SkString TessellationTestRectShader::getTessEvaluationShaderGLSL( |
Chris Dalton | 54c9093 | 2020-06-24 10:51:52 -0600 | [diff] [blame] | 249 | const GrGLSLPrimitiveProcessor*, const char* versionAndExtensionDecls, |
| 250 | const GrGLSLUniformHandler&, const GrShaderCaps& caps) const { |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 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 | |
| 279 | void TessellationTestRectShader::Impl::writeFragmentShader( |
| 280 | GrGLSLFPFragmentBuilder* f, const char* color, const char* coverage) { |
| 281 | f->declareGlobal(GrShaderVar("barycentric_coord", kFloat4_GrSLType, |
Ben Wagner | dabd98c | 2020-03-25 15:17:18 -0400 | [diff] [blame] | 282 | GrShaderVar::TypeModifier::In)); |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 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: |
| 300 | TessellationTestOp(const SkMatrix& viewMatrix, const std::array<float, 3>* triPositions) |
Robert Phillips | 709e240 | 2020-03-23 18:29:16 +0000 | [diff] [blame] | 301 | : GrDrawOp(ClassID()), fViewMatrix(viewMatrix), fTriPositions(triPositions) { |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 302 | this->setBounds(SkRect::MakeIWH(kWidth, kHeight), HasAABloat::kNo, IsHairline::kNo); |
| 303 | } |
| 304 | |
| 305 | private: |
| 306 | const char* name() const override { return "TessellationTestOp"; } |
| 307 | FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; } |
Robert Phillips | 709e240 | 2020-03-23 18:29:16 +0000 | [diff] [blame] | 308 | GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, |
| 309 | bool hasMixedSampledCoverage, GrClampType) override { |
| 310 | return GrProcessorSet::EmptySetAnalysis(); |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 311 | } |
| 312 | |
Robert Phillips | 709e240 | 2020-03-23 18:29:16 +0000 | [diff] [blame] | 313 | void onPrePrepare(GrRecordingContext*, |
Brian Salomon | 8afde5f | 2020-04-01 16:22:00 -0400 | [diff] [blame] | 314 | const GrSurfaceProxyView* writeView, |
Robert Phillips | 709e240 | 2020-03-23 18:29:16 +0000 | [diff] [blame] | 315 | GrAppliedClip*, |
| 316 | const GrXferProcessor::DstProxyView&) override {} |
Robert Phillips | c655c3a | 2020-03-18 13:23:45 -0400 | [diff] [blame] | 317 | |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 318 | 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 Phillips | 709e240 | 2020-03-23 18:29:16 +0000 | [diff] [blame] | 328 | GrPipeline pipeline(GrScissorTest::kDisabled, SkBlendMode::kSrc, |
Brian Salomon | 982f546 | 2020-03-30 12:52:33 -0400 | [diff] [blame] | 329 | state->drawOpArgs().writeSwizzle()); |
Robert Phillips | 709e240 | 2020-03-23 18:29:16 +0000 | [diff] [blame] | 330 | 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 Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 343 | } |
| 344 | |
Robert Phillips | 709e240 | 2020-03-23 18:29:16 +0000 | [diff] [blame] | 345 | GrProgramInfo programInfo(state->proxy()->numSamples(), state->proxy()->numStencilSamples(), |
Brian Salomon | 8afde5f | 2020-04-01 16:22:00 -0400 | [diff] [blame] | 346 | state->proxy()->backendFormat(), state->writeView()->origin(), |
Robert Phillips | 709e240 | 2020-03-23 18:29:16 +0000 | [diff] [blame] | 347 | &pipeline, shader.get(), GrPrimitiveType::kPatches, |
| 348 | tessellationPatchVertexCount); |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 349 | |
Robert Phillips | 709e240 | 2020-03-23 18:29:16 +0000 | [diff] [blame] | 350 | state->bindPipeline(programInfo, SkRect::MakeIWH(kWidth, kHeight)); |
Chris Dalton | aa0e45c | 2020-03-16 10:05:11 -0600 | [diff] [blame] | 351 | state->bindBuffers(nullptr, nullptr, fVertexBuffer.get()); |
Robert Phillips | 709e240 | 2020-03-23 18:29:16 +0000 | [diff] [blame] | 352 | state->draw(tessellationPatchVertexCount, fBaseVertex); |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 353 | } |
| 354 | |
| 355 | const SkMatrix fViewMatrix; |
| 356 | const std::array<float, 3>* const fTriPositions; |
| 357 | sk_sp<const GrBuffer> fVertexBuffer; |
| 358 | int fBaseVertex = 0; |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 359 | }; |
| 360 | |
| 361 | |
| 362 | static 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 | |
Robert Phillips | 95c250c | 2020-06-29 15:36:12 -0400 | [diff] [blame] | 381 | DrawResult TessellationGM::onDraw(GrRecordingContext* ctx, GrRenderTargetContext* rtc, |
| 382 | SkCanvas* canvas, SkString* errorMsg) { |
Chris Dalton | 5a2f962 | 2019-12-27 14:56:38 -0700 | [diff] [blame] | 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 | |
| 414 | DEF_GM( return new TessellationGM(); ) |
| 415 | |
| 416 | } |