1 /* 2 * Copyright 2017 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 "SkSLCompiler.h" 9 10 #include "Test.h" 11 12 static void test(skiatest::Reporter* r, const char* src, const GrShaderCaps& caps, 13 std::vector<const char*> expectedH, std::vector<const char*> expectedCPP) { 14 SkSL::Program::Settings settings; 15 settings.fCaps = ∩︀ 16 SkSL::Compiler compiler; 17 SkSL::StringStream output; 18 std::unique_ptr<SkSL::Program> program = compiler.convertProgram( 19 SkSL::Program::kFragmentProcessor_Kind, 20 SkSL::String(src), 21 settings); 22 if (!program) { 23 SkDebugf("Unexpected error compiling %s\n%s", src, compiler.errorText().c_str()); 24 return; 25 } 26 REPORTER_ASSERT(r, program); 27 bool success = compiler.toH(*program, "Test", output); 28 if (!success) { 29 SkDebugf("Unexpected error compiling %s\n%s", src, compiler.errorText().c_str()); 30 } 31 REPORTER_ASSERT(r, success); 32 if (success) { 33 for (const char* expected : expectedH) { 34 bool found = strstr(output.str().c_str(), expected); 35 if (!found) { 36 SkDebugf("HEADER MISMATCH:\nsource:\n%s\n\nexpected:\n'%s'\n\nreceived:\n'%s'", src, 37 expected, output.str().c_str()); 38 } 39 REPORTER_ASSERT(r, found); 40 } 41 } 42 output.reset(); 43 success = compiler.toCPP(*program, "Test", output); 44 if (!success) { 45 SkDebugf("Unexpected error compiling %s\n%s", src, compiler.errorText().c_str()); 46 } 47 REPORTER_ASSERT(r, success); 48 if (success) { 49 for (const char* expected : expectedCPP) { 50 bool found = strstr(output.str().c_str(), expected); 51 if (!found) { 52 SkDebugf("CPP MISMATCH:\nsource:\n%s\n\nexpected:\n'%s'\n\nreceived:\n'%s'", src, 53 expected, output.str().c_str()); 54 } 55 REPORTER_ASSERT(r, found); 56 } 57 } 58 } 59 60 DEF_TEST(SkSLFPHelloWorld, r) { 61 test(r, 62 "/* HEADER */" 63 "void main() {" 64 "sk_OutColor = half4(1);" 65 "}", 66 *SkSL::ShaderCapsFactory::Default(), 67 { 68 "/* HEADER */\n" 69 "\n" 70 "/**************************************************************************************************\n" 71 " *** This file was autogenerated from GrTest.fp; do not modify.\n" 72 " **************************************************************************************************/\n" 73 "#ifndef GrTest_DEFINED\n" 74 "#define GrTest_DEFINED\n" 75 "#include \"SkTypes.h\"\n" 76 "#include \"GrFragmentProcessor.h\"\n" 77 "#include \"GrCoordTransform.h\"\n" 78 "class GrTest : public GrFragmentProcessor {\n" 79 "public:\n" 80 " static std::unique_ptr<GrFragmentProcessor> Make() {\n" 81 " return std::unique_ptr<GrFragmentProcessor>(new GrTest());\n" 82 " }\n" 83 " GrTest(const GrTest& src);\n" 84 " std::unique_ptr<GrFragmentProcessor> clone() const override;\n" 85 " const char* name() const override { return \"Test\"; }\n" 86 "private:\n" 87 " GrTest()\n" 88 " : INHERITED(kGrTest_ClassID, kNone_OptimizationFlags) {\n" 89 " }\n" 90 " GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;\n" 91 " void onGetGLSLProcessorKey(const GrShaderCaps&,GrProcessorKeyBuilder*) " 92 "const override;\n" 93 " bool onIsEqual(const GrFragmentProcessor&) const override;\n" 94 " GR_DECLARE_FRAGMENT_PROCESSOR_TEST\n" 95 " typedef GrFragmentProcessor INHERITED;\n" 96 "};\n" 97 "#endif\n" 98 }, 99 { 100 "/* HEADER */\n" 101 "\n" 102 "/**************************************************************************************************\n" 103 " *** This file was autogenerated from GrTest.fp; do not modify.\n" 104 " **************************************************************************************************/\n" 105 "#include \"GrTest.h\"\n" 106 "#include \"glsl/GrGLSLFragmentProcessor.h\"\n" 107 "#include \"glsl/GrGLSLFragmentShaderBuilder.h\"\n" 108 "#include \"glsl/GrGLSLProgramBuilder.h\"\n" 109 "#include \"GrTexture.h\"\n" 110 "#include \"SkSLCPP.h\"\n" 111 "#include \"SkSLUtil.h\"\n" 112 "class GrGLSLTest : public GrGLSLFragmentProcessor {\n" 113 "public:\n" 114 " GrGLSLTest() {}\n" 115 " void emitCode(EmitArgs& args) override {\n" 116 " GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;\n" 117 " const GrTest& _outer = args.fFp.cast<GrTest>();\n" 118 " (void) _outer;\n" 119 " fragBuilder->codeAppendf(\"%s = half4(1.0);\\n\", args.fOutputColor);\n" 120 " }\n" 121 "private:\n" 122 " void onSetData(const GrGLSLProgramDataManager& pdman, " 123 "const GrFragmentProcessor& _proc) override {\n" 124 " }\n" 125 "};\n" 126 "GrGLSLFragmentProcessor* GrTest::onCreateGLSLInstance() const {\n" 127 " return new GrGLSLTest();\n" 128 "}\n" 129 "void GrTest::onGetGLSLProcessorKey(const GrShaderCaps& caps, " 130 "GrProcessorKeyBuilder* b) const {\n" 131 "}\n" 132 "bool GrTest::onIsEqual(const GrFragmentProcessor& other) const {\n" 133 " const GrTest& that = other.cast<GrTest>();\n" 134 " (void) that;\n" 135 " return true;\n" 136 "}\n" 137 "GrTest::GrTest(const GrTest& src)\n" 138 ": INHERITED(kGrTest_ClassID, src.optimizationFlags()) {\n" 139 "}\n" 140 "std::unique_ptr<GrFragmentProcessor> GrTest::clone() const {\n" 141 " return std::unique_ptr<GrFragmentProcessor>(new GrTest(*this));\n" 142 "}\n" 143 }); 144 } 145 146 DEF_TEST(SkSLFPInput, r) { 147 test(r, 148 "in half2 point;" 149 "void main() {" 150 "sk_OutColor = half4(point, point);" 151 "}", 152 *SkSL::ShaderCapsFactory::Default(), 153 { 154 "const SkPoint& point() const { return fPoint; }", 155 "static std::unique_ptr<GrFragmentProcessor> Make(SkPoint point) {", 156 "return std::unique_ptr<GrFragmentProcessor>(new GrTest(point));", 157 "GrTest(SkPoint point)", 158 ", fPoint(point)" 159 }, 160 { 161 "fragBuilder->codeAppendf(\"%s = half4(half2(%f, %f), half2(%f, %f));\\n\", " 162 "args.fOutputColor, _outer.point().fX, _outer.point().fY, " 163 "_outer.point().fX, _outer.point().fY);", 164 "if (fPoint != that.fPoint) return false;" 165 }); 166 } 167 168 DEF_TEST(SkSLFPUniform, r) { 169 test(r, 170 "uniform half4 color;" 171 "void main() {" 172 "sk_OutColor = color;" 173 "}", 174 *SkSL::ShaderCapsFactory::Default(), 175 { 176 "static std::unique_ptr<GrFragmentProcessor> Make()" 177 }, 178 { 179 "fColorVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType, " 180 "\"color\");", 181 }); 182 } 183 184 // SkSLFPInUniform tests the simplest plumbing case, default type, no tracking 185 // with a setUniform template that supports inlining the value call with no 186 // local variable. 187 DEF_TEST(SkSLFPInUniform, r) { 188 test(r, 189 "in uniform half4 color;" 190 "void main() {" 191 "sk_OutColor = color;" 192 "}", 193 *SkSL::ShaderCapsFactory::Default(), 194 { 195 "static std::unique_ptr<GrFragmentProcessor> Make(SkRect color) {", 196 }, 197 { 198 "fColorVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType, " 199 "\"color\");", 200 "pdman.set4fv(fColorVar, 1, reinterpret_cast<const float*>(&(_outer.color())));" 201 }); 202 } 203 204 // As above, but tests in uniform's ability to override the default ctype. 205 DEF_TEST(SkSLFPInUniformCType, r) { 206 test(r, 207 "layout(ctype=SkPMColor4f) in uniform half4 color;" 208 "void main() {" 209 "sk_OutColor = color;" 210 "}", 211 *SkSL::ShaderCapsFactory::Default(), 212 { 213 "static std::unique_ptr<GrFragmentProcessor> Make(SkPMColor4f color) {", 214 }, 215 { 216 "fColorVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType, " 217 "\"color\");", 218 "pdman.set4fv(fColorVar, 1, (_outer.color()).vec());" 219 }); 220 } 221 222 // Add state tracking to the default typed SkRect <-> half4 uniform. But since 223 // it now has to track state, the value inlining previously done for the 224 // setUniform call is removed in favor of a local variable. 225 DEF_TEST(SkSLFPTrackedInUniform, r) { 226 test(r, 227 "layout(tracked) in uniform half4 color;" 228 "void main() {" 229 "sk_OutColor = color;" 230 "}", 231 *SkSL::ShaderCapsFactory::Default(), 232 { 233 "static std::unique_ptr<GrFragmentProcessor> Make(SkRect color) {", 234 }, 235 { 236 "SkRect fColorPrev = SkRect::MakeEmpty();", 237 "fColorVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType, " 238 "\"color\");", 239 "const SkRect& colorValue = _outer.color();", 240 "if (fColorPrev.isEmpty() || fColorPrev != colorValue) {", 241 "fColorPrev = colorValue;", 242 "pdman.set4fv(fColorVar, 1, reinterpret_cast<const float*>(&colorValue));" 243 }); 244 } 245 246 // Test the case where the template does not support variable inlining in 247 // setUniform (i.e. it references the value multiple times). 248 DEF_TEST(SkSLFPNonInlinedInUniform, r) { 249 test(r, 250 "in uniform half2 point;" 251 "void main() {" 252 "sk_OutColor = half4(point, point);" 253 "}", 254 *SkSL::ShaderCapsFactory::Default(), 255 { 256 "static std::unique_ptr<GrFragmentProcessor> Make(SkPoint point) {", 257 }, 258 { 259 "fPointVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf2_GrSLType, " 260 "\"point\");", 261 "const SkPoint& pointValue = _outer.point();", 262 "pdman.set2f(fPointVar, pointValue.fX, pointValue.fY);" 263 }); 264 } 265 266 // Test handling conditional uniforms (that use when= in layout), combined with 267 // state tracking and custom ctypes to really put the code generation through its paces. 268 DEF_TEST(SkSLFPConditionalInUniform, r) { 269 test(r, 270 "in bool test;" 271 "layout(ctype=SkPMColor4f, tracked, when=test) in uniform half4 color;" 272 "void main() {" 273 " if (test) {" 274 " sk_OutColor = color;" 275 " } else {" 276 " sk_OutColor = half4(1);" 277 " }" 278 "}", 279 *SkSL::ShaderCapsFactory::Default(), 280 { 281 "static std::unique_ptr<GrFragmentProcessor> Make(bool test, SkPMColor4f color) {", 282 }, 283 { 284 "SkPMColor4f fColorPrev = {SK_FloatNaN, SK_FloatNaN, SK_FloatNaN, SK_FloatNaN}", 285 "auto test = _outer.test();", 286 "if (test) {", 287 "fColorVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType, " 288 "\"color\");", 289 "if (fColorVar.isValid()) {", 290 "const SkPMColor4f& colorValue = _outer.color();", 291 "if (fColorPrev != colorValue) {", 292 "fColorPrev = colorValue;", 293 "pdman.set4fv(fColorVar, 1, colorValue.vec());" 294 }); 295 } 296 297 DEF_TEST(SkSLFPSections, r) { 298 test(r, 299 "@header { header section }" 300 "void main() {" 301 "sk_OutColor = half4(1);" 302 "}", 303 *SkSL::ShaderCapsFactory::Default(), 304 { 305 "header section" 306 }, 307 {}); 308 test(r, 309 "@class { class section }" 310 "void main() {" 311 "sk_OutColor = half4(1);" 312 "}", 313 *SkSL::ShaderCapsFactory::Default(), 314 { 315 "class GrTest : public GrFragmentProcessor {\n" 316 "public:\n" 317 " class section" 318 }, 319 {}); 320 test(r, 321 "@cpp { cpp section }" 322 "void main() {" 323 "sk_OutColor = half4(1);" 324 "}", 325 *SkSL::ShaderCapsFactory::Default(), 326 {}, 327 {"cpp section"}); 328 test(r, 329 "@constructorParams { int x, float y, std::vector<float> z }" 330 "in float w;" 331 "void main() {" 332 "sk_OutColor = half4(1);" 333 "}", 334 *SkSL::ShaderCapsFactory::Default(), 335 { 336 "Make(float w, int x, float y, std::vector<float> z )", 337 "return std::unique_ptr<GrFragmentProcessor>(new GrTest(w, x, y, z));", 338 "GrTest(float w, int x, float y, std::vector<float> z )", 339 ", fW(w) {" 340 }, 341 {}); 342 test(r, 343 "@constructor { constructor section }" 344 "void main() {" 345 "sk_OutColor = half4(1);" 346 "}", 347 *SkSL::ShaderCapsFactory::Default(), 348 { 349 "private:\n constructor section" 350 }, 351 {}); 352 test(r, 353 "@initializers { initializers section }" 354 "void main() {" 355 "sk_OutColor = half4(1);" 356 "}", 357 *SkSL::ShaderCapsFactory::Default(), 358 { 359 ": INHERITED(kGrTest_ClassID, kNone_OptimizationFlags)\n , initializers section" 360 }, 361 {}); 362 test(r, 363 "half x = 10;" 364 "@emitCode { fragBuilder->codeAppendf(\"half y = %d\\n\", x * 2); }" 365 "void main() {" 366 "sk_OutColor = half4(1);" 367 "}", 368 *SkSL::ShaderCapsFactory::Default(), 369 {}, 370 { 371 "x = 10.0;\n" 372 " fragBuilder->codeAppendf(\"half y = %d\\n\", x * 2);" 373 }); 374 test(r, 375 "@fields { fields section }" 376 "@clone { }" 377 "void main() {" 378 "sk_OutColor = half4(1);" 379 "}", 380 *SkSL::ShaderCapsFactory::Default(), 381 { 382 "GR_DECLARE_FRAGMENT_PROCESSOR_TEST\n" 383 " fields section typedef GrFragmentProcessor INHERITED;" 384 }, 385 {}); 386 test(r, 387 "@make { make section }" 388 "void main() {" 389 "sk_OutColor = half4(1);" 390 "}", 391 *SkSL::ShaderCapsFactory::Default(), 392 { 393 "public:\n" 394 " make section" 395 }, 396 {}); 397 test(r, 398 "uniform half calculated;" 399 "in half provided;" 400 "@setData(varName) { varName.set1f(calculated, provided * 2); }" 401 "void main() {" 402 "sk_OutColor = half4(1);" 403 "}", 404 *SkSL::ShaderCapsFactory::Default(), 405 {}, 406 { 407 "void onSetData(const GrGLSLProgramDataManager& varName, " 408 "const GrFragmentProcessor& _proc) override {\n", 409 "UniformHandle& calculated = fCalculatedVar;", 410 "auto provided = _outer.provided();", 411 "varName.set1f(calculated, provided * 2);" 412 }); 413 test(r, 414 "@test(testDataName) { testDataName section }" 415 "void main() {" 416 "sk_OutColor = half4(1);" 417 "}", 418 *SkSL::ShaderCapsFactory::Default(), 419 {}, 420 { 421 "#if GR_TEST_UTILS\n" 422 "std::unique_ptr<GrFragmentProcessor> GrTest::TestCreate(GrProcessorTestData* testDataName) {\n" 423 " testDataName section }\n" 424 "#endif" 425 }); 426 } 427 428 DEF_TEST(SkSLFPTransformedCoords, r) { 429 test(r, 430 "void main() {" 431 "sk_OutColor = half4(sk_TransformedCoords2D[0], sk_TransformedCoords2D[0]);" 432 "}", 433 *SkSL::ShaderCapsFactory::Default(), 434 {}, 435 { 436 "SkString sk_TransformedCoords2D_0 = " 437 "fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);", 438 "fragBuilder->codeAppendf(\"%s = half4(%s, %s);\\n\", args.fOutputColor, " 439 "sk_TransformedCoords2D_0.c_str(), sk_TransformedCoords2D_0.c_str());" 440 }); 441 442 } 443 444 DEF_TEST(SkSLFPLayoutWhen, r) { 445 test(r, 446 "layout(when=someExpression(someOtherExpression())) uniform half sometimes;" 447 "void main() {" 448 "}", 449 *SkSL::ShaderCapsFactory::Default(), 450 {}, 451 { 452 "if (someExpression(someOtherExpression())) {\n" 453 " fSometimesVar = args.fUniformHandler->addUniform" 454 }); 455 456 } 457 458 DEF_TEST(SkSLFPChildProcessors, r) { 459 test(r, 460 "in fragmentProcessor child1;" 461 "in fragmentProcessor child2;" 462 "void main() {" 463 " sk_OutColor = process(child1) * process(child2);" 464 "}", 465 *SkSL::ShaderCapsFactory::Default(), 466 { 467 "this->registerChildProcessor(std::move(child1));", 468 "this->registerChildProcessor(std::move(child2));" 469 }, 470 { 471 "SkString _child0(\"_child0\");", 472 "this->emitChild(_outer.child1_index(), &_child0, args);", 473 "SkString _child1(\"_child1\");", 474 "this->emitChild(_outer.child2_index(), &_child1, args);", 475 "this->registerChildProcessor(src.childProcessor(fChild1_index).clone());", 476 "this->registerChildProcessor(src.childProcessor(fChild2_index).clone());" 477 }); 478 } 479 480 DEF_TEST(SkSLFPChildProcessorsWithInput, r) { 481 test(r, 482 "in fragmentProcessor child1;" 483 "in fragmentProcessor child2;" 484 "void main() {" 485 " half4 childIn = sk_InColor;" 486 " half4 childOut1 = process(child1, childIn);" 487 " half4 childOut2 = process(child2, childOut1);" 488 " sk_OutColor = childOut2;" 489 "}", 490 *SkSL::ShaderCapsFactory::Default(), 491 { 492 "this->registerChildProcessor(std::move(child1));", 493 "this->registerChildProcessor(std::move(child2));" 494 }, 495 { 496 "SkString _input0(\"childIn\");", 497 "SkString _child0(\"_child0\");", 498 "this->emitChild(_outer.child1_index(), _input0.c_str(), &_child0, args);", 499 "SkString _input1(\"childOut1\");", 500 "SkString _child1(\"_child1\");", 501 "this->emitChild(_outer.child2_index(), _input1.c_str(), &_child1, args);", 502 "this->registerChildProcessor(src.childProcessor(fChild1_index).clone());", 503 "this->registerChildProcessor(src.childProcessor(fChild2_index).clone());" 504 }); 505 } 506 507 DEF_TEST(SkSLFPChildProcessorWithInputExpression, r) { 508 test(r, 509 "in fragmentProcessor child;" 510 "void main() {" 511 " sk_OutColor = process(child, sk_InColor * half4(0.5));" 512 "}", 513 *SkSL::ShaderCapsFactory::Default(), 514 { 515 "this->registerChildProcessor(std::move(child));", 516 }, 517 { 518 "SkString _input0 = SkStringPrintf(\"%s * half4(0.5)\", args.fInputColor);", 519 "SkString _child0(\"_child0\");", 520 "this->emitChild(_outer.child_index(), _input0.c_str(), &_child0, args);", 521 "this->registerChildProcessor(src.childProcessor(fChild_index).clone());", 522 }); 523 } 524 525 DEF_TEST(SkSLFPNestedChildProcessors, r) { 526 test(r, 527 "in fragmentProcessor child1;" 528 "in fragmentProcessor child2;" 529 "void main() {" 530 " sk_OutColor = process(child2, sk_InColor * process(child1, sk_InColor * half4(0.5)));" 531 "}", 532 *SkSL::ShaderCapsFactory::Default(), 533 { 534 "this->registerChildProcessor(std::move(child1));", 535 "this->registerChildProcessor(std::move(child2));" 536 }, 537 { 538 "SkString _input0 = SkStringPrintf(\"%s * half4(0.5)\", args.fInputColor);", 539 "SkString _child0(\"_child0\");", 540 "this->emitChild(_outer.child1_index(), _input0.c_str(), &_child0, args);", 541 "SkString _input1 = SkStringPrintf(\"%s * %s\", args.fInputColor, _child0.c_str());", 542 "SkString _child1(\"_child1\");", 543 "this->emitChild(_outer.child2_index(), _input1.c_str(), &_child1, args);", 544 "this->registerChildProcessor(src.childProcessor(fChild1_index).clone());", 545 "this->registerChildProcessor(src.childProcessor(fChild2_index).clone());" 546 }); 547 } 548 549 DEF_TEST(SkSLFPChildFPAndGlobal, r) { 550 test(r, 551 "in fragmentProcessor child;" 552 "bool hasCap = sk_Caps.externalTextureSupport;" 553 "void main() {" 554 " if (hasCap) {" 555 " sk_OutColor = process(child, sk_InColor);" 556 " } else {" 557 " sk_OutColor = half4(1);" 558 " }" 559 "}", 560 *SkSL::ShaderCapsFactory::Default(), 561 { 562 "this->registerChildProcessor(std::move(child));" 563 }, 564 { 565 "hasCap = sk_Caps.externalTextureSupport;", 566 "fragBuilder->codeAppendf(\"bool hasCap = %s;\\nif (hasCap) {\", " 567 "(hasCap ? \"true\" : \"false\"));", 568 "SkString _input0 = SkStringPrintf(\"%s\", args.fInputColor);", 569 "SkString _child0(\"_child0\");", 570 "this->emitChild(_outer.child_index(), _input0.c_str(), &_child0, args);", 571 "fragBuilder->codeAppendf(\"\\n %s = %s;\\n} else {\\n %s = half4(1.0);\\n}" 572 "\\n\", args.fOutputColor, _child0.c_str(), args.fOutputColor);", 573 "this->registerChildProcessor(src.childProcessor(fChild_index).clone());" 574 }); 575 } 576 577 DEF_TEST(SkSLFPChildProcessorInlineFieldAccess, r) { 578 test(r, 579 "in fragmentProcessor child;" 580 "void main() {" 581 " if (child.preservesOpaqueInput) {" 582 " sk_OutColor = process(child, sk_InColor);" 583 " } else {" 584 " sk_OutColor = half4(1);" 585 " }" 586 "}", 587 *SkSL::ShaderCapsFactory::Default(), 588 { 589 "this->registerChildProcessor(std::move(child));" 590 }, 591 { 592 "fragBuilder->codeAppendf(\"if (%s) {\", " 593 "(_outer.childProcessor(_outer.child_index()).preservesOpaqueInput() ? " 594 "\"true\" : \"false\"));", 595 "SkString _input0 = SkStringPrintf(\"%s\", args.fInputColor);", 596 "SkString _child0(\"_child0\");", 597 "this->emitChild(_outer.child_index(), _input0.c_str(), &_child0, args);", 598 "fragBuilder->codeAppendf(\"\\n %s = %s;\\n} else {\\n %s = half4(1.0);\\n}\\n\"" 599 ", args.fOutputColor, _child0.c_str(), args.fOutputColor);", 600 "this->registerChildProcessor(src.childProcessor(fChild_index).clone());" 601 }); 602 } 603 604 DEF_TEST(SkSLFPChildProcessorFieldAccess, r) { 605 test(r, 606 "in fragmentProcessor child;" 607 "bool opaque = child.preservesOpaqueInput;" 608 "void main() {" 609 " if (opaque) {" 610 " sk_OutColor = process(child);" 611 " } else {" 612 " sk_OutColor = half4(0.5);" 613 " }" 614 "}", 615 *SkSL::ShaderCapsFactory::Default(), 616 { 617 "this->registerChildProcessor(std::move(child));" 618 }, 619 { 620 "opaque = _outer.childProcessor(_outer.child_index()).preservesOpaqueInput();", 621 "fragBuilder->codeAppendf(\"bool opaque = %s;\\nif (opaque) {\", " 622 "(opaque ? \"true\" : \"false\"));", 623 "SkString _child0(\"_child0\");", 624 "this->emitChild(_outer.child_index(), &_child0, args);", 625 "fragBuilder->codeAppendf(\"\\n %s = %s;\\n} else {\\n %s = half4(0.5);\\n}\\n\"" 626 ", args.fOutputColor, _child0.c_str(), args.fOutputColor);", 627 "this->registerChildProcessor(src.childProcessor(fChild_index).clone());" 628 }); 629 } 630 631 DEF_TEST(SkSLFPNullableChildProcessor, r) { 632 test(r, 633 "in fragmentProcessor? child;" 634 "void main() {" 635 " if (child != null) {" 636 " sk_OutColor = process(child);" 637 " } else {" 638 " sk_OutColor = half4(0.5);" 639 " }" 640 "}", 641 *SkSL::ShaderCapsFactory::Default(), 642 {}, 643 { 644 "SkString _child0(\"_child0\");", 645 "if (_outer.child_index() >= 0) {", 646 "this->emitChild(_outer.child_index(), &_child0, args);", 647 "} else {", 648 "fragBuilder->codeAppendf(\"half4 %s;\", _child0.c_str());", 649 "}", 650 "fragBuilder->codeAppendf(\"\\n %s = %s;\\n} else {\\n %s = half4(0.5);\\n}\\n\"" 651 ", args.fOutputColor, _child0.c_str(), args.fOutputColor);", 652 }); 653 } 654