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