| Ethan Nicholas | 624a529 | 2021-04-16 14:54:43 -0400 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright 2019 Google LLC | 
|  | 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 "include/core/SkBitmap.h" | 
|  | 9 | #include "include/core/SkCanvas.h" | 
|  | 10 | #include "include/core/SkColorFilter.h" | 
|  | 11 | #include "include/core/SkData.h" | 
|  | 12 | #include "include/core/SkPaint.h" | 
|  | 13 | #include "include/core/SkSurface.h" | 
|  | 14 | #include "include/effects/SkRuntimeEffect.h" | 
|  | 15 | #include "include/gpu/GrDirectContext.h" | 
|  | 16 | #include "include/sksl/DSLRuntimeEffects.h" | 
|  | 17 | #include "src/core/SkTLazy.h" | 
|  | 18 | #include "src/gpu/GrColor.h" | 
|  | 19 | #include "src/sksl/SkSLCompiler.h" | 
|  | 20 | #include "tests/Test.h" | 
|  | 21 |  | 
|  | 22 | #include <algorithm> | 
|  | 23 | #include <thread> | 
|  | 24 |  | 
|  | 25 | using namespace SkSL::dsl; | 
|  | 26 |  | 
|  | 27 | class DSLTestEffect { | 
|  | 28 | public: | 
|  | 29 | DSLTestEffect(skiatest::Reporter* r, sk_sp<SkSurface> surface) | 
|  | 30 | : fReporter(r) | 
|  | 31 | , fCaps(SkSL::ShaderCapsFactory::Standalone()) | 
|  | 32 | , fCompiler(std::make_unique<SkSL::Compiler>(fCaps.get())) | 
|  | 33 | , fSurface(std::move(surface)) {} | 
|  | 34 |  | 
|  | 35 | void start() { | 
|  | 36 | StartRuntimeShader(fCompiler.get()); | 
|  | 37 | } | 
|  | 38 |  | 
|  | 39 | void end() { | 
|  | 40 | sk_sp<SkRuntimeEffect> effect = EndRuntimeShader(); | 
|  | 41 | SkASSERT(effect); | 
|  | 42 | fBuilder.init(std::move(effect)); | 
|  | 43 | } | 
|  | 44 |  | 
|  | 45 | SkRuntimeShaderBuilder::BuilderUniform uniform(const char* name) { | 
|  | 46 | return fBuilder->uniform(name); | 
|  | 47 | } | 
|  | 48 | SkRuntimeShaderBuilder::BuilderChild child(const char* name) { | 
|  | 49 | return fBuilder->child(name); | 
|  | 50 | } | 
|  | 51 |  | 
|  | 52 | using PreTestFn = std::function<void(SkCanvas*, SkPaint*)>; | 
|  | 53 |  | 
|  | 54 | void test(GrColor TL, GrColor TR, GrColor BL, GrColor BR, | 
|  | 55 | PreTestFn preTestCallback = nullptr) { | 
|  | 56 | auto shader = fBuilder->makeShader(nullptr, false); | 
|  | 57 | if (!shader) { | 
|  | 58 | REPORT_FAILURE(fReporter, "shader", SkString("Effect didn't produce a shader")); | 
|  | 59 | return; | 
|  | 60 | } | 
|  | 61 |  | 
|  | 62 | SkCanvas* canvas = fSurface->getCanvas(); | 
|  | 63 | SkPaint paint; | 
|  | 64 | paint.setShader(std::move(shader)); | 
|  | 65 | paint.setBlendMode(SkBlendMode::kSrc); | 
|  | 66 |  | 
|  | 67 | canvas->save(); | 
|  | 68 | if (preTestCallback) { | 
|  | 69 | preTestCallback(canvas, &paint); | 
|  | 70 | } | 
|  | 71 | canvas->drawPaint(paint); | 
|  | 72 | canvas->restore(); | 
|  | 73 |  | 
|  | 74 | GrColor actual[4]; | 
|  | 75 | SkImageInfo info = fSurface->imageInfo(); | 
|  | 76 | if (!fSurface->readPixels(info, actual, info.minRowBytes(), 0, 0)) { | 
|  | 77 | REPORT_FAILURE(fReporter, "readPixels", SkString("readPixels failed")); | 
|  | 78 | return; | 
|  | 79 | } | 
|  | 80 |  | 
|  | 81 | GrColor expected[4] = {TL, TR, BL, BR}; | 
|  | 82 | if (0 != memcmp(actual, expected, sizeof(actual))) { | 
|  | 83 | REPORT_FAILURE(fReporter, "Runtime effect didn't match expectations", | 
|  | 84 | SkStringPrintf("\n" | 
|  | 85 | "Expected: [ %08x %08x %08x %08x ]\n" | 
|  | 86 | "Got     : [ %08x %08x %08x %08x ]\n" | 
|  | 87 | "SkSL:\n%s\n", | 
|  | 88 | TL, TR, BL, BR, actual[0], actual[1], actual[2], | 
|  | 89 | actual[3], fBuilder->effect()->source().c_str())); | 
|  | 90 | } | 
|  | 91 | } | 
|  | 92 |  | 
|  | 93 | void test(GrColor expected, PreTestFn preTestCallback = nullptr) { | 
|  | 94 | this->test(expected, expected, expected, expected, preTestCallback); | 
|  | 95 | } | 
|  | 96 |  | 
|  | 97 | private: | 
|  | 98 | skiatest::Reporter*             fReporter; | 
|  | 99 | SkSL::ShaderCapsPointer         fCaps; | 
|  | 100 | std::unique_ptr<SkSL::Compiler> fCompiler; | 
|  | 101 | sk_sp<SkSurface>                fSurface; | 
|  | 102 | SkTLazy<SkRuntimeShaderBuilder> fBuilder; | 
|  | 103 | }; | 
|  | 104 |  | 
|  | 105 | static void test_RuntimeEffect_Shaders(skiatest::Reporter* r, GrRecordingContext* rContext) { | 
|  | 106 | SkImageInfo info = SkImageInfo::Make(2, 2, kRGBA_8888_SkColorType, kPremul_SkAlphaType); | 
|  | 107 | sk_sp<SkSurface> surface = rContext | 
|  | 108 | ? SkSurface::MakeRenderTarget(rContext, SkBudgeted::kNo, info) | 
|  | 109 | : SkSurface::MakeRaster(info); | 
|  | 110 | REPORTER_ASSERT(r, surface); | 
|  | 111 | using float4 = std::array<float, 4>; | 
|  | 112 | using int4 = std::array<int, 4>; | 
|  | 113 | DSLTestEffect effect(r, surface); | 
|  | 114 |  | 
|  | 115 | // Local coords | 
|  | 116 | { | 
|  | 117 | effect.start(); | 
|  | 118 | Var p(kFloat2_Type, "p"); | 
|  | 119 | Function(kHalf4_Type, "main", p).define( | 
|  | 120 | Return(Half4(Half2(p - 0.5), 0, 1)) | 
|  | 121 | ); | 
|  | 122 | effect.end(); | 
|  | 123 | effect.test(0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF); | 
|  | 124 | } | 
|  | 125 |  | 
|  | 126 | // Use of a simple uniform. (Draw twice with two values to ensure it's updated). | 
|  | 127 | { | 
|  | 128 | effect.start(); | 
|  | 129 | Var gColor(kUniform_Modifier, kFloat4_Type); | 
| Ethan Nicholas | e9c2c5a | 2021-04-30 13:14:24 -0400 | [diff] [blame] | 130 | DeclareGlobal(gColor); | 
| Ethan Nicholas | 371f6e1 | 2021-05-04 14:30:02 -0400 | [diff] [blame^] | 131 | Var p(kFloat2_Type, "p"); | 
|  | 132 | Function(kHalf4_Type, "main", p).define( | 
| Ethan Nicholas | 624a529 | 2021-04-16 14:54:43 -0400 | [diff] [blame] | 133 | Return(Half4(gColor)) | 
|  | 134 | ); | 
|  | 135 | effect.end(); | 
|  | 136 | effect.uniform(gColor.name()) = float4{ 0.0f, 0.25f, 0.75f, 1.0f }; | 
|  | 137 | effect.test(0xFFBF4000); | 
|  | 138 | effect.uniform(gColor.name()) = float4{ 1.0f, 0.0f, 0.0f, 0.498f }; | 
|  | 139 | effect.test(0x7F00007F);  // Tests that we clamp to valid premul | 
|  | 140 | } | 
|  | 141 |  | 
|  | 142 | // Same, with integer uniforms | 
|  | 143 | { | 
|  | 144 | effect.start(); | 
|  | 145 | Var gColor(kUniform_Modifier, kInt4_Type); | 
| Ethan Nicholas | e9c2c5a | 2021-04-30 13:14:24 -0400 | [diff] [blame] | 146 | DeclareGlobal(gColor); | 
| Ethan Nicholas | 371f6e1 | 2021-05-04 14:30:02 -0400 | [diff] [blame^] | 147 | Var p(kFloat2_Type, "p"); | 
|  | 148 | Function(kHalf4_Type, "main", p).define( | 
| Ethan Nicholas | 624a529 | 2021-04-16 14:54:43 -0400 | [diff] [blame] | 149 | Return(Half4(gColor) / 255) | 
|  | 150 | ); | 
|  | 151 | effect.end(); | 
|  | 152 | effect.uniform(gColor.name()) = int4{ 0x00, 0x40, 0xBF, 0xFF }; | 
|  | 153 | effect.test(0xFFBF4000); | 
|  | 154 | effect.uniform(gColor.name()) = int4{ 0xFF, 0x00, 0x00, 0x7F }; | 
|  | 155 | effect.test(0x7F00007F);  // Tests that we clamp to valid premul | 
|  | 156 | } | 
|  | 157 |  | 
|  | 158 | // Test sk_FragCoord (device coords). Rotate the canvas to be sure we're seeing device coords. | 
|  | 159 | // Since the surface is 2x2, we should see (0,0), (1,0), (0,1), (1,1). Multiply by 0.498 to | 
|  | 160 | // make sure we're not saturating unexpectedly. | 
|  | 161 | { | 
|  | 162 | effect.start(); | 
| Ethan Nicholas | 371f6e1 | 2021-05-04 14:30:02 -0400 | [diff] [blame^] | 163 | Var p(kFloat2_Type, "p"); | 
|  | 164 | Function(kHalf4_Type, "main", p).define( | 
| Ethan Nicholas | 624a529 | 2021-04-16 14:54:43 -0400 | [diff] [blame] | 165 | Return(Half4(0.498 * (Half2(Swizzle(sk_FragCoord(), X, Y)) - 0.5), 0, 1)) | 
|  | 166 | ); | 
|  | 167 | effect.end(); | 
|  | 168 | effect.test(0xFF000000, 0xFF00007F, 0xFF007F00, 0xFF007F7F, | 
|  | 169 | [](SkCanvas* canvas, SkPaint*) { canvas->rotate(45.0f); }); | 
|  | 170 | } | 
|  | 171 |  | 
|  | 172 | // Runtime effects should use relaxed precision rules by default | 
|  | 173 | { | 
|  | 174 | effect.start(); | 
|  | 175 | Var p(kFloat2_Type, "p"); | 
|  | 176 | Function(kHalf4_Type, "main", p).define( | 
|  | 177 | Return(Float4(p - 0.5, 0, 1)) | 
|  | 178 | ); | 
|  | 179 | effect.end(); | 
|  | 180 | effect.test(0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF); | 
|  | 181 | } | 
|  | 182 |  | 
|  | 183 | // ... and support *returning* float4, not just half4 | 
|  | 184 | { | 
|  | 185 | effect.start(); | 
|  | 186 | Var p(kFloat2_Type, "p"); | 
|  | 187 | Function(kFloat4_Type, "main", p).define( | 
|  | 188 | Return(Float4(p - 0.5, 0, 1)) | 
|  | 189 | ); | 
|  | 190 | effect.end(); | 
|  | 191 | effect.test(0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF); | 
|  | 192 | } | 
|  | 193 |  | 
|  | 194 | // Mutating coords should work. (skbug.com/10918) | 
|  | 195 | { | 
|  | 196 | effect.start(); | 
|  | 197 | Var p(kFloat2_Type, "p"); | 
|  | 198 | Function(kFloat4_Type, "main", p).define( | 
|  | 199 | p -= 0.5, | 
|  | 200 | Return(Float4(p, 0, 1)) | 
|  | 201 | ); | 
|  | 202 | effect.end(); | 
|  | 203 | effect.test(0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF); | 
|  | 204 | } | 
|  | 205 | { | 
|  | 206 | effect.start(); | 
|  | 207 | Var p1(kInOut_Modifier, kFloat2_Type, "p"); | 
|  | 208 | Function moveCoords(kVoid_Type, "moveCoords", p1); | 
|  | 209 | moveCoords.define( | 
|  | 210 | p1 -= 0.5 | 
|  | 211 | ); | 
|  | 212 | Var p2(kFloat2_Type, "p"); | 
|  | 213 | Function(kFloat4_Type, "main", p2).define( | 
|  | 214 | moveCoords(p2), | 
|  | 215 | Return(Float4(p2, 0, 1)) | 
|  | 216 | ); | 
|  | 217 | effect.end(); | 
|  | 218 | effect.test(0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF); | 
|  | 219 | } | 
|  | 220 |  | 
|  | 221 | // | 
|  | 222 | // Sampling children | 
|  | 223 | // | 
|  | 224 |  | 
|  | 225 | // Sampling a null child should return the paint color | 
|  | 226 | { | 
|  | 227 | effect.start(); | 
|  | 228 | Var child(kUniform_Modifier, kShader_Type, "child"); | 
| Ethan Nicholas | e9c2c5a | 2021-04-30 13:14:24 -0400 | [diff] [blame] | 229 | DeclareGlobal(child); | 
| Brian Osman | 552fcb9 | 2021-04-28 17:41:57 -0400 | [diff] [blame] | 230 | Var p2(kFloat2_Type, "p"); | 
|  | 231 | Function(kFloat4_Type, "main", p2).define( | 
|  | 232 | Return(Sample(child, p2)) | 
| Ethan Nicholas | 624a529 | 2021-04-16 14:54:43 -0400 | [diff] [blame] | 233 | ); | 
|  | 234 | effect.end(); | 
|  | 235 | effect.child("child") = nullptr; | 
|  | 236 | effect.test(0xFF00FFFF, | 
|  | 237 | [](SkCanvas*, SkPaint* paint) { paint->setColor4f({1.0f, 1.0f, 0.0f, 1.0f}); }); | 
|  | 238 | } | 
|  | 239 | } | 
|  | 240 |  | 
|  | 241 | DEF_TEST(DSLRuntimeEffectSimple, r) { | 
|  | 242 | test_RuntimeEffect_Shaders(r, nullptr); | 
|  | 243 | } | 
|  | 244 |  | 
|  | 245 | DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DSLRuntimeEffectSimple_GPU, r, ctxInfo) { | 
|  | 246 | test_RuntimeEffect_Shaders(r, ctxInfo.directContext()); | 
|  | 247 | } |