/*
 * Copyright 2022 Google LLC
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "tests/Test.h"

#if defined(SK_GRAPHITE)

#include "include/core/SkColorSpace.h"
#include "include/effects/SkRuntimeEffect.h"
#include "include/gpu/graphite/precompile/PrecompileBlender.h"
#include "include/gpu/graphite/precompile/PrecompileColorFilter.h"
#include "include/gpu/graphite/precompile/PrecompileRuntimeEffect.h"
#include "include/gpu/graphite/precompile/PrecompileShader.h"
#include "src/gpu/graphite/ContextPriv.h"
#include "src/gpu/graphite/KeyContext.h"
#include "src/gpu/graphite/PipelineData.h"
#include "src/gpu/graphite/PrecompileInternal.h"
#include "src/gpu/graphite/RenderPassDesc.h"
#include "src/gpu/graphite/Renderer.h"
#include "src/gpu/graphite/RuntimeEffectDictionary.h"
#include "src/gpu/graphite/precompile/PaintOptionsPriv.h"

#include <array>

using namespace::skgpu::graphite;

namespace {

// colorfilters
static constexpr int kExpectedBlendCFCombos = 15;
static constexpr int kExpectedColorSpaceCFCombos = 1;
static constexpr int kExpectedHighContrastCFCombos = 1;
static constexpr int kExpectedLightingCFCombos = 1;
static constexpr int kExpectedLumaCFCombos = 1;
static constexpr int kExpectedMatrixCFCombos = 1;
static constexpr int kExpectedOverdrawCFCombos = 1;
static constexpr int kExpectedTableCFCombos = 1;

// shaders
static constexpr int kExpectedGradientCombos = 3;
static constexpr int kExpectedImageCombos = 24;
static constexpr int kExpectedPerlinNoiseCombos = 1;
static constexpr int kExpectedPictureCombos = 48;
static constexpr int kExpectedRawImageCombos = 10;
static constexpr int kExpectedSolidColorCombos = 1;

// A default kSrcOver blend mode will be supplied if no other blend options are added
void no_blend_mode_option_test(const KeyContext& keyContext,
                               const RenderPassDesc& renderPassDesc,
                               skiatest::Reporter* reporter) {
    PaintOptions paintOptions;
    paintOptions.setShaders({{ PrecompileShaders::Color() }});

    REPORTER_ASSERT(reporter, paintOptions.priv().numCombinations() == 1);

    std::vector<UniquePaintParamsID> precompileIDs;
    paintOptions.priv().buildCombinations(keyContext,
                                          DrawTypeFlags::kNone,
                                          /* withPrimitiveBlender= */ false,
                                          Coverage::kNone,
                                          renderPassDesc,
                                          [&precompileIDs](UniquePaintParamsID id,
                                                           DrawTypeFlags,
                                                           bool /* withPrimitiveBlender */,
                                                           Coverage,
                                                           const RenderPassDesc&) {
                                                               precompileIDs.push_back(id);
                                                           });

    SkASSERT(precompileIDs.size() == 1);
}

// This test checks that the 'PaintOptions::numCombinations' method and the number actually
// generated by 'buildCombinations' agree with the expected number of combinations.
void run_test(const KeyContext& keyContext,
              const RenderPassDesc& renderPassDesc,
              skiatest::Reporter* reporter,
              const PaintOptions& paintOptions,
              int expectedNumOptions) {
    REPORTER_ASSERT(reporter, paintOptions.priv().numCombinations() == expectedNumOptions,
                    "expected %d, but was %d",
                    expectedNumOptions, paintOptions.priv().numCombinations());

    std::vector<UniquePaintParamsID> precompileIDs;
    paintOptions.priv().buildCombinations(keyContext,
                                          DrawTypeFlags::kNone,
                                          /* withPrimitiveBlender= */ false,
                                          Coverage::kNone,
                                          renderPassDesc,
                                          [&precompileIDs](UniquePaintParamsID id,
                                                           DrawTypeFlags,
                                                           bool /* withPrimitiveBlender */,
                                                           Coverage,
                                                           const RenderPassDesc&) {
                                              precompileIDs.push_back(id);
                                          });

    SkASSERT(static_cast<int>(precompileIDs.size()) == expectedNumOptions);
}

void big_test(const KeyContext& keyContext,
              const RenderPassDesc& renderPassDesc,
              skiatest::Reporter* reporter) {

    static constexpr int kNumExpected = 1596;
    // paintOptions (1596 = 4*399)
    //  |- (399 = 3+396) sweepGrad_0 (3) |
    //  |                blendShader_0 (396 = 1*4*99)
    //  |                 |- 0: (1)       kSrc (1)
    //  |                 |- 1: (4=3+1)   (dsts) linearGrad_0 (3) | solid_0 (1)
    //  |                 |- 2: (99=3+96) (srcs) linearGrad_1 (3) |
    //  |                                        blendShader_1 (96=1*4*24)
    //  |                                         |- 0: (1) kDst (1)
    //  |                                         |- 1: (4=3+1) (dsts) radGrad_0 (3) | solid_1 (1)
    //  |                                         |- 2: (24) (srcs) imageShader_0 (24)
    //  |
    //  |- (4) 4-built-in-blend-modes

    PaintOptions paintOptions;

    // first, shaders. First top-level option (sweepGrad_0)
    sk_sp<PrecompileShader> sweepGrad_0 = PrecompileShaders::SweepGradient();

    std::array<SkBlendMode, 1> blendModes{ SkBlendMode::kSrc };

    std::vector<SkBlendMode> moreBlendModes{ SkBlendMode::kDst };

    // Second top-level option (blendShader_0)
    auto blendShader_0 = PrecompileShaders::Blend(
                                SkSpan<const SkBlendMode>(blendModes),          // std::array
                                {{                                               // initializer_list
                                    PrecompileShaders::LinearGradient(),
                                    PrecompileShaders::Color()
                                }},
                                {{
                                    PrecompileShaders::LinearGradient(),
                                    PrecompileShaders::Blend(
                                            SkSpan<const SkBlendMode>(moreBlendModes),// std::vector
                                            {{
                                                PrecompileShaders::RadialGradient(),
                                                PrecompileShaders::Color()
                                            }},
                                            {{
                                                PrecompileShaders::Image()
                                            }})
                                }});

    paintOptions.setShaders({{ sweepGrad_0, blendShader_0 }});

    static const SkBlendMode kEvenMoreBlendModes[] = {
        SkBlendMode::kSrcOver,
        SkBlendMode::kSrc,
        SkBlendMode::kDstOver,
        SkBlendMode::kDst
    };

    // now, blend modes
    paintOptions.setBlendModes(kEvenMoreBlendModes);                             // c array

    REPORTER_ASSERT(reporter, paintOptions.priv().numCombinations() == kNumExpected,
                    "Actual # of combinations %d, expected %d",
                    paintOptions.priv().numCombinations(),
                    kNumExpected);

    std::vector<UniquePaintParamsID> precompileIDs;
    paintOptions.priv().buildCombinations(keyContext,
                                          DrawTypeFlags::kNone,
                                          /* withPrimitiveBlender= */ false,
                                          Coverage::kNone,
                                          renderPassDesc,
                                          [&precompileIDs](UniquePaintParamsID id,
                                                           DrawTypeFlags,
                                                           bool /* withPrimitiveBlender */,
                                                           Coverage,
                                                           const RenderPassDesc&) {
                                                               precompileIDs.push_back(id);
                                                           });

    SkASSERT(precompileIDs.size() == kNumExpected);
}

template <typename T>
std::vector<sk_sp<T>> create_runtime_combos(
        skiatest::Reporter* reporter,
        SkRuntimeEffect::Result effectFactory(SkString),
        sk_sp<T> precompileFactory(sk_sp<SkRuntimeEffect>,
                                   SkSpan<const PrecompileChildOptions> childOptions),
        const char* redCode,
        const char* greenCode,
        const char* blueCode,
        const char* combineCode) {
    auto [redEffect, error1] = effectFactory(SkString(redCode));
    REPORTER_ASSERT(reporter, redEffect, "%s", error1.c_str());
    auto [greenEffect, error2] = effectFactory(SkString(greenCode));
    REPORTER_ASSERT(reporter, greenEffect, "%s", error2.c_str());
    auto [blueEffect, error3] = effectFactory(SkString(blueCode));
    REPORTER_ASSERT(reporter, blueEffect, "%s", error3.c_str());
    auto [combineEffect, error4] = effectFactory(SkString(combineCode));
    REPORTER_ASSERT(reporter, combineEffect, "%s", error4.c_str());

    sk_sp<T> red = precompileFactory(redEffect, {});
    REPORTER_ASSERT(reporter, red);

    sk_sp<T> green = precompileFactory(greenEffect, {});
    REPORTER_ASSERT(reporter, green);

    sk_sp<T> blue = precompileFactory(blueEffect, {});
    REPORTER_ASSERT(reporter, blue);

    sk_sp<T> combine = precompileFactory(combineEffect, {{ {{ red, green }},
                                                           {{ blue, sk_sp<T>(nullptr) }} }});
    REPORTER_ASSERT(reporter, combine);

    return { combine };
}

void runtime_effect_test(const KeyContext& keyContext,
                         const RenderPassDesc& renderPassDesc,
                         skiatest::Reporter* reporter) {
    // paintOptions (64 = 4*4*4)
    //  |- combineShader (4)
    //  |       0: redShader  | greenShader
    //  |       1: blueShader | nullptr
    //  |
    //  |- combineColorFilter (4)
    //  |       0: redColorFilter  | greenColorFilter
    //  |       1: blueColorFilter | nullptr
    //  |
    //  |- combineBlender (4)
    //  |       0: redBlender  | greenBlender
    //  |       1: blueBlender | nullptr

    PaintOptions paintOptions;

    // shaders
    {
        static const char* kRedS = R"(
            half4 main(vec2 fragcoord) { return half4(.5, 0, 0, .5); }
        )";
        static const char* kGreenS = R"(
            half4 main(vec2 fragcoord) { return half4(0, .5, 0, .5); }
        )";
        static const char* kBlueS = R"(
            half4 main(vec2 fragcoord) { return half4(0, 0, .5, .5); }
        )";

        static const char* kCombineS = R"(
            uniform shader first;
            uniform shader second;
            half4 main(vec2 fragcoords) {
                return first.eval(fragcoords) + second.eval(fragcoords);
            }
        )";

        std::vector<sk_sp<PrecompileShader>> combinations =
            create_runtime_combos<PrecompileShader>(reporter,
                                                    SkRuntimeEffect::MakeForShader,
                                                    PrecompileRuntimeEffects::MakePrecompileShader,
                                                    kRedS,
                                                    kGreenS,
                                                    kBlueS,
                                                    kCombineS);
        paintOptions.setShaders(combinations);
    }

    // color filters
    {
        static const char* kRedCF = R"(
            half4 main(half4 color) { return half4(.5, 0, 0, .5); }
        )";
        static const char* kGreenCF = R"(
            half4 main(half4 color) { return half4(0, .5, 0, .5); }
        )";
        static const char* kBlueCF = R"(
            half4 main(half4 color) { return half4(0, 0, .5, .5); }
        )";

        static const char* kCombineCF = R"(
            uniform colorFilter first;
            uniform colorFilter second;
            half4 main(half4 color) { return first.eval(color) + second.eval(color); }
        )";

        std::vector<sk_sp<PrecompileColorFilter>> combinations =
            create_runtime_combos<PrecompileColorFilter>(
                    reporter,
                    SkRuntimeEffect::MakeForColorFilter,
                    PrecompileRuntimeEffects::MakePrecompileColorFilter,
                    kRedCF,
                    kGreenCF,
                    kBlueCF,
                    kCombineCF);
        paintOptions.setColorFilters(combinations);
    }

    // blenders
    {
        static const char* kRedB = R"(
            half4 main(half4 src, half4 dst) { return half4(.5, 0, 0, .5); }
        )";
        static const char* kGreenB = R"(
            half4 main(half4 src, half4 dst) { return half4(0, .5, 0, .5); }
        )";
        static const char* kBlueB = R"(
            half4 main(half4 src, half4 dst) { return half4(0, 0, .5, .5); }
        )";

        static const char* kCombineB = R"(
            uniform blender first;
            uniform blender second;
            half4 main(half4 src, half4 dst) {
                return first.eval(src, dst) + second.eval(src, dst);
            }
        )";

        std::vector<sk_sp<PrecompileBlender>> combinations =
            create_runtime_combos<PrecompileBlender>(
                    reporter,
                    SkRuntimeEffect::MakeForBlender,
                    PrecompileRuntimeEffects::MakePrecompileBlender,
                    kRedB,
                    kGreenB,
                    kBlueB,
                    kCombineB);
        paintOptions.setBlenders(combinations);
    }

    REPORTER_ASSERT(reporter, paintOptions.priv().numCombinations() == 64);

    std::vector<UniquePaintParamsID> precompileIDs;
    paintOptions.priv().buildCombinations(keyContext,
                                          DrawTypeFlags::kNone,
                                          /* withPrimitiveBlender= */ false,
                                          Coverage::kNone,
                                          renderPassDesc,
                                          [&precompileIDs](UniquePaintParamsID id,
                                                           DrawTypeFlags,
                                                           bool /* withPrimitiveBlender */,
                                                           Coverage,
                                                           const RenderPassDesc&) {
                                                               precompileIDs.push_back(id);
                                                           });

    SkASSERT(precompileIDs.size() == 64);
}

// Exercise all the PrecompileBlenders factories
void blend_subtest(const KeyContext& keyContext,
                   const RenderPassDesc& renderPassDesc,
                   skiatest::Reporter* reporter) {
    // The BlendMode PrecompileBlender only ever has 1 combination
    {
        PaintOptions paintOptions;
        paintOptions.setBlenders({{ PrecompileBlenders::Mode(SkBlendMode::kColorDodge) }});

        run_test(keyContext, renderPassDesc, reporter, paintOptions, /* expectedNumOptions= */ 1);
    }

    // Specifying the BlendMode PrecompileBlender by SkBlendMode should also only ever
    // yield 1 combination.
    {
        PaintOptions paintOptions;
        paintOptions.setBlendModes(SKSPAN_INIT_ONE( SkBlendMode::kSrcOver ));

        run_test(keyContext, renderPassDesc, reporter, paintOptions, /* expectedNumOptions= */ 1);
    }

    // The Arithmetic PrecompileBlender only ever has 1 combination
    {
        PaintOptions paintOptions;
        paintOptions.setBlenders(SKSPAN_INIT_ONE( PrecompileBlenders::Arithmetic() ));

        run_test(keyContext, renderPassDesc, reporter, paintOptions, /* expectedNumOptions= */ 1);
    }
}

// Exercise all the PrecompileShaders factories
void shader_subtest(const KeyContext& keyContext,
                    const RenderPassDesc& renderPassDesc,
                    skiatest::Reporter* reporter) {
    {
        PaintOptions paintOptions;
        paintOptions.setShaders({{ PrecompileShaders::Empty() }});

        run_test(keyContext, renderPassDesc, reporter, paintOptions, /* expectedNumOptions= */ 1);
    }

    // The solid color shader only ever generates one combination. Because it is constant
    // everywhere it can cause other shaders to be elided (e.g., the LocalMatrix shader -
    // see the LocalMatrix test(s) below).
    {
        PaintOptions paintOptions;
        paintOptions.setShaders({{ PrecompileShaders::Color() }});

        run_test(keyContext, renderPassDesc, reporter, paintOptions,
                 /* expectedNumOptions= */ kExpectedSolidColorCombos);
    }

    // In general, the blend shader generates the product of the options in each of its slots.
    // The rules for how many combinations the SkBlendModes yield are:
    //   all Porter-Duff SkBlendModes collapse to one option (see GetPorterDuffBlendConstants))
    //   all HSCL SkBlendModes collapse to another option
    //   all other SkBlendModes produce unique options
    {
        const SkBlendMode kBlendModes[] = {
                SkBlendMode::kSrcOut,    // Porter-Duff
                SkBlendMode::kSrcOver,   // Porter-Duff
                SkBlendMode::kHue,       // HSLC
                SkBlendMode::kColor,     // HSLC
                SkBlendMode::kScreen,    // Fixed Screen
                SkBlendMode::kDarken,    // fixed Darken
        };
        PaintOptions paintOptions;
        paintOptions.setShaders({{
                PrecompileShaders::Blend(SkSpan<const SkBlendMode>(kBlendModes),
                                         {{ PrecompileShaders::Color() }},
                                         {{ PrecompileShaders::MakeFractalNoise() }})
        }});

        run_test(keyContext, renderPassDesc, reporter, paintOptions,
                 /* expectedNumOptions= */ 4 *  // Porter-Duff, HSLC, Screen, Darken
                                           kExpectedSolidColorCombos *
                                           kExpectedPerlinNoiseCombos);
    }

    // The ImageShaders have 24 combinations
    // (6 sampling/tiling x 4 color space xform)
    // The CoordClamp shader doesn't add any additional combinations to its wrapped shader.
    {
        PaintOptions paintOptions;
        paintOptions.setShaders({{
            PrecompileShaders::CoordClamp({{ PrecompileShaders::Image() }})
        }});

        run_test(keyContext, renderPassDesc, reporter, paintOptions,
                 /* expectedNumOptions= */ kExpectedImageCombos);
    }

    // RawImageShaders only have 10 combinations since they never do cubic filtering and only have
    // two possible color space xform variants.
    {
        PaintOptions paintOptions;
        paintOptions.setShaders({{ PrecompileShaders::RawImage() }});

        run_test(keyContext, renderPassDesc, reporter, paintOptions,
                 /* expectedNumOptions= */ kExpectedRawImageCombos);
    }

    // Each Perlin noise shader only has one combination
    {
        PaintOptions paintOptions;
        paintOptions.setShaders({{ PrecompileShaders::MakeFractalNoise(),
                                   PrecompileShaders::MakeTurbulence() }});

        run_test(keyContext, renderPassDesc, reporter, paintOptions,
                 /* expectedNumOptions= */ kExpectedPerlinNoiseCombos + kExpectedPerlinNoiseCombos);
    }

    // Each gradient shader generates 3 combinations
    {
        PaintOptions paintOptions;
        paintOptions.setShaders({{ PrecompileShaders::LinearGradient(),
                                   PrecompileShaders::RadialGradient(),
                                   PrecompileShaders::TwoPointConicalGradient(),
                                   PrecompileShaders::SweepGradient() }});

        run_test(keyContext, renderPassDesc, reporter, paintOptions,
                 /* expectedNumOptions= */ kExpectedGradientCombos + kExpectedGradientCombos +
                                           kExpectedGradientCombos + kExpectedGradientCombos);
    }

    // Each picture shader generates 48 combinations:
    //    2 (pictureShader LM) x 24 (imageShader variations)
    {
        PaintOptions paintOptions;
        paintOptions.setShaders({{ PrecompileShaders::Picture() }});

        run_test(keyContext, renderPassDesc, reporter, paintOptions,
                 /* expectedNumOptions= */ kExpectedPictureCombos);
    }

    // In general, the local matrix shader just generates however many options its wrapped
    // shader generates.
    {
        PaintOptions paintOptions;
        paintOptions.setShaders({{
                PrecompileShaders::LocalMatrix({{ PrecompileShaders::LinearGradient() }}) }});

        run_test(keyContext, renderPassDesc, reporter, paintOptions,
                 /* expectedNumOptions= */ kExpectedGradientCombos);
    }

    // The ColorFilter shader just creates the cross product of its child options
    {
        PaintOptions paintOptions;
        paintOptions.setShaders(
                {{ PrecompileShaders::ColorFilter({{ PrecompileShaders::LinearGradient() }},
                                                  {{ PrecompileColorFilters::Blend() }}) }});

        run_test(keyContext, renderPassDesc, reporter, paintOptions,
                 /* expectedNumOptions= */ kExpectedGradientCombos * kExpectedBlendCFCombos);
    }

    {
        PaintOptions paintOptions;
        paintOptions.setShaders({{
                PrecompileShaders::WorkingColorSpace({{ PrecompileShaders::LinearGradient() }},
                                                     {{ SkColorSpace::MakeSRGBLinear() }}) }});

        run_test(keyContext, renderPassDesc, reporter, paintOptions,
                 /* expectedNumOptions= */ kExpectedGradientCombos *
                                           1 /* only one colorSpace */);
    }
}

// Exercise all the PrecompileColorFilters factories. The impact of colorfilters on the number
// of combinations is very predictable. Except for the Compose and Lerp color filters, all the
// color filters only ever have one combination. The Compose and Lerp color filters also just
// simply generate the cross product of their children.
void colorfilter_subtest(const KeyContext& keyContext,
                         const RenderPassDesc& renderPassDesc,
                         skiatest::Reporter* reporter) {


    {
        // The compose colorfilter just generates the cross product of its children.
        static constexpr int kExpectedNumOptions =
                kExpectedTableCFCombos * (kExpectedHighContrastCFCombos + kExpectedLumaCFCombos) +
                kExpectedLightingCFCombos * (kExpectedHighContrastCFCombos + kExpectedLumaCFCombos);

        PaintOptions paintOptions;
        paintOptions.setColorFilters(
            {{ PrecompileColorFilters::Compose(
                {{ PrecompileColorFilters::Table(), PrecompileColorFilters::Lighting() }},
                {{ PrecompileColorFilters::HighContrast(), PrecompileColorFilters::Luma() }}) }});

        run_test(keyContext, renderPassDesc, reporter, paintOptions, kExpectedNumOptions);
    }

    {
        PaintOptions paintOptions;
        paintOptions.setColorFilters({{ PrecompileColorFilters::Blend() }});

        run_test(keyContext, renderPassDesc, reporter, paintOptions,
                 kExpectedBlendCFCombos);
    }

    {
        PaintOptions paintOptions;
        paintOptions.setColorFilters({{ PrecompileColorFilters::Matrix(),
                                        PrecompileColorFilters::HSLAMatrix() }});

        // HSLAMatrix and Matrix map to the same color filter
        run_test(keyContext, renderPassDesc, reporter, paintOptions,
                 kExpectedMatrixCFCombos + kExpectedMatrixCFCombos);
    }

    {
        PaintOptions paintOptions;
        paintOptions.setColorFilters({{ PrecompileColorFilters::LinearToSRGBGamma(),
                                         PrecompileColorFilters::SRGBToLinearGamma() }});

        // LinearToSRGB and SRGBToLinear both map to the colorspace colorfilter
        run_test(keyContext, renderPassDesc, reporter, paintOptions,
                 kExpectedColorSpaceCFCombos + kExpectedColorSpaceCFCombos);
    }

    {
        // The lerp colorfilter just generates the cross product of its children.
        static constexpr int kExpectedNumOptions =
                kExpectedMatrixCFCombos * (kExpectedBlendCFCombos + kExpectedOverdrawCFCombos) +
                kExpectedLumaCFCombos * (kExpectedBlendCFCombos + kExpectedOverdrawCFCombos);

        PaintOptions paintOptions;
        paintOptions.setColorFilters(
            {{ PrecompileColorFilters::Lerp(
                 {{ PrecompileColorFilters::Matrix(), PrecompileColorFilters::Luma() }},
                 {{ PrecompileColorFilters::Blend(), PrecompileColorFilters::Overdraw() }}) }});

        run_test(keyContext, renderPassDesc, reporter, paintOptions, kExpectedNumOptions);
    }
}

} // anonymous namespace

DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTS(CombinationBuilderTest, reporter, context,
                                   CtsEnforcement::kNever) {
    ShaderCodeDictionary* dict = context->priv().shaderCodeDictionary();

    sk_sp<RuntimeEffectDictionary> rtEffectDict = sk_make_sp<RuntimeEffectDictionary>();

    SkColorInfo ci(kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr);
    FloatStorageManager floatStorageManager;
    PaintParamsKeyBuilder builder(dict);
    PipelineDataGatherer gatherer(Layout::kMetal);
    KeyContext keyContext(context->priv().caps(), &floatStorageManager, &builder, &gatherer, dict,
                          rtEffectDict, ci);

    RenderPassDesc unusedRenderPassDesc;

    // The default PaintOptions should create a single combination with a solid color shader and
    // kSrcOver blending
    {
        PaintOptions paintOptions;

        run_test(keyContext, unusedRenderPassDesc, reporter, paintOptions,
                 /* expectedNumOptions= */ 1);
    }

    blend_subtest(keyContext, unusedRenderPassDesc, reporter);
    shader_subtest(keyContext, unusedRenderPassDesc, reporter);
    colorfilter_subtest(keyContext, unusedRenderPassDesc, reporter);

    no_blend_mode_option_test(keyContext, unusedRenderPassDesc, reporter);
    big_test(keyContext, unusedRenderPassDesc, reporter);
    runtime_effect_test(keyContext, unusedRenderPassDesc, reporter);
}

#endif // SK_GRAPHITE
