Support structs in runtime effects

Uses the pipeline-stage callback mechanism. It mangles the type name
(with a test to verify that this works), and then calls defineStruct
with the entire SkSL struct definition string.

Bug: skia:10939
Change-Id: If14cf1b11faaa80ad8d4086cdacf68532bac43fc
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/368809
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: John Stiles <johnstiles@google.com>
diff --git a/tests/SkRuntimeEffectTest.cpp b/tests/SkRuntimeEffectTest.cpp
index a36f03f..f364c53 100644
--- a/tests/SkRuntimeEffectTest.cpp
+++ b/tests/SkRuntimeEffectTest.cpp
@@ -401,3 +401,44 @@
     REPORTER_ASSERT(r, c.fB == 0.5625f);
     REPORTER_ASSERT(r, c.fA == 1.0f);
 }
+
+static void test_RuntimeEffectStructNameReuse(skiatest::Reporter* r, GrRecordingContext* rContext) {
+    // Test that two different runtime effects can reuse struct names in a single paint operation
+    auto [childEffect, err] = SkRuntimeEffect::Make(SkString(
+        "uniform shader paint;"
+        "struct S { half4 rgba; };"
+        "void process(inout S s) { s.rgba.rgb *= 0.5; }"
+        "half4 main() { S s; s.rgba = sample(paint); process(s); return s.rgba; }"
+    ));
+    REPORTER_ASSERT(r, childEffect, "%s\n", err.c_str());
+    sk_sp<SkShader> nullChild = nullptr;
+    sk_sp<SkShader> child = childEffect->makeShader(/*uniforms=*/nullptr, &nullChild,
+                                                    /*childCount=*/1, /*localMatrix=*/nullptr,
+                                                    /*isOpaque=*/false);
+
+    SkImageInfo info = SkImageInfo::Make(2, 2, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+    sk_sp<SkSurface> surface = rContext
+                                    ? SkSurface::MakeRenderTarget(rContext, SkBudgeted::kNo, info)
+                                    : SkSurface::MakeRaster(info);
+    REPORTER_ASSERT(r, surface);
+
+    TestEffect effect(r, surface);
+    effect.build(
+            "uniform shader child;"
+            "struct S { float2 coord; };"
+            "void process(inout S s) { s.coord = s.coord.yx; }"
+            "half4 main(float2 p) { S s; s.coord = p; process(s); return sample(child, s.coord); "
+            "}");
+    effect.child("child") = child;
+    effect.test(0xFF00407F, [](SkCanvas*, SkPaint* paint) {
+        paint->setColor4f({0.99608f, 0.50196f, 0.0f, 1.0f});
+    });
+}
+
+DEF_TEST(SkRuntimeStructNameReuse, r) {
+    test_RuntimeEffectStructNameReuse(r, nullptr);
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRuntimeStructNameReuse_GPU, r, ctxInfo) {
+    test_RuntimeEffectStructNameReuse(r, ctxInfo.directContext());
+}