| /* | |
| * Copyright 2025 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/SkCanvas.h" | |
| #include "include/core/SkColorSpace.h" | |
| #include "include/core/SkString.h" | |
| #include "include/effects/SkRuntimeEffect.h" | |
| #include "include/gpu/graphite/Context.h" | |
| #include "include/gpu/graphite/PrecompileContext.h" | |
| #include "include/gpu/graphite/Surface.h" | |
| #include "include/gpu/graphite/precompile/PaintOptions.h" | |
| #include "include/gpu/graphite/precompile/Precompile.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/core/SkRuntimeEffectPriv.h" | |
| #include "src/gpu/graphite/ContextPriv.h" | |
| #include "src/gpu/graphite/PrecompileContextPriv.h" | |
| #include "src/gpu/graphite/ShaderCodeDictionary.h" | |
| #include "tools/graphite/ContextFactory.h" | |
| #include "tools/graphite/GraphiteToolUtils.h" | |
| #include "tools/graphite/PipelineCallbackHandler.h" | |
| #include "tools/graphite/UniqueKeyUtils.h" | |
| #include "tools/graphite/precompile/PrecompileEffectFactories.h" | |
| extern bool gGraphiteAvoidDepth; | |
| using namespace::skgpu::graphite; | |
| using namespace skiatest::graphite; | |
| using namespace skiatools::graphite; | |
| namespace { | |
| std::pair<SkPaint, PaintOptions> create_paint_and_options(bool addBlenders) { | |
| SkPaint paint; | |
| PaintOptions paintOptions; | |
| auto [shader, shaderOption] = PrecompileFactories::CreateAnnulusRuntimeShader(); | |
| paint.setShader(std::move(shader)); | |
| paintOptions.setShaders({{ std::move(shaderOption) }}); | |
| auto [colorFilter, colorFilterOption] = PrecompileFactories::CreateComboRuntimeColorFilter(); | |
| paint.setColorFilter(std::move(colorFilter)); | |
| paintOptions.setColorFilters({{ std::move(colorFilterOption) }}); | |
| if (addBlenders) { | |
| auto [blender, blenderOption] = PrecompileFactories::CreateComboRuntimeBlender(); | |
| paint.setBlender(std::move(blender)); | |
| paintOptions.setBlenders({{ std::move(blenderOption) }}); | |
| } | |
| return { paint, paintOptions }; | |
| } | |
| bool draw_with_normal_api(skgpu::graphite::Context* context, | |
| skgpu::graphite::Recorder* recorder, | |
| const SkPaint& paint) { | |
| auto ii = SkImageInfo::Make({ 256, 256 }, kRGBA_8888_SkColorType, kPremul_SkAlphaType); | |
| sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(recorder, ii, skgpu::Mipmapped::kNo); | |
| if (!surface) { | |
| return false; | |
| } | |
| SkCanvas* canvas = surface->getCanvas(); | |
| canvas->drawRect({0, 0, 100, 100}, paint); | |
| std::unique_ptr<skgpu::graphite::Recording> recording = recorder->snap(); | |
| if (!recording) { | |
| return false; | |
| } | |
| if (!context->insertRecording({ recording.get() })) { | |
| return false; | |
| } | |
| if (!context->submit(skgpu::graphite::SyncToCpu::kYes)) { | |
| return false; | |
| } | |
| return true; | |
| } | |
| void fetch_keys_and_reset(skiatest::Reporter* reporter, | |
| PipelineCallBackHandler* handler, | |
| PrecompileContext* precompileContext, | |
| std::vector<skgpu::UniqueKey>* uniqueKeys, | |
| std::vector<sk_sp<SkData>>* serializedKeys, | |
| bool reset) { | |
| UniqueKeyUtils::FetchUniqueKeys(precompileContext, uniqueKeys); | |
| handler->retrieveKeys(serializedKeys); | |
| if (reset) { | |
| GlobalCache* globalCache = precompileContext->priv().globalCache(); | |
| globalCache->resetGraphicsPipelines(); | |
| REPORTER_ASSERT(reporter, globalCache->numGraphicsPipelines() == 0); | |
| handler->reset(); | |
| } | |
| } | |
| // Get the existing keys, reset, and try recreating them all w/ the serialized pipeline keys | |
| void reset_and_recreate_pipelines_with_serialized_keys( | |
| skiatest::Reporter* reporter, | |
| Context* context, | |
| Recorder* recorder, | |
| PrecompileContext* precompileContext, | |
| PipelineCallBackHandler* handler) { | |
| GlobalCache* globalCache = precompileContext->priv().globalCache(); | |
| ShaderCodeDictionary* shaderCodeDictionary = context->priv().shaderCodeDictionary(); | |
| auto [paint, _] = create_paint_and_options(/* addBlenders= */ true); | |
| draw_with_normal_api(context, recorder, paint); | |
| // None of the user-defined stable runtime effects should've been transmuted to not-stable | |
| REPORTER_ASSERT(reporter, !shaderCodeDictionary->numUserDefinedRuntimeEffects()); | |
| std::vector<skgpu::UniqueKey> origKeys; | |
| std::vector<sk_sp<SkData>> androidStyleKeys; | |
| fetch_keys_and_reset(reporter, handler, precompileContext, | |
| &origKeys, &androidStyleKeys, /* reset= */ true); | |
| // Given 'draw_with_normal_api' we expect one serialized key - full of user-defined stable keys | |
| REPORTER_ASSERT(reporter, origKeys.size() == 1); | |
| REPORTER_ASSERT(reporter, androidStyleKeys.size() == 1); | |
| // Use the serialized keys to regenerate the Pipelines | |
| for (sk_sp<SkData>& d : androidStyleKeys) { | |
| bool result = precompileContext->precompile(d); | |
| SkAssertResult(result); | |
| } | |
| // We need to explicitly wait for the precompilation to finish here | |
| context->priv().sharedContext()->pipelineManager()->wait_TestOnly(); | |
| // None of the user-defined stable runtime effects should've been transmuted to not-stable | |
| REPORTER_ASSERT(reporter, !shaderCodeDictionary->numUserDefinedRuntimeEffects()); | |
| std::vector<skgpu::UniqueKey> recreatedKeys; | |
| std::vector<sk_sp<SkData>> recreatedAndroidStyleKeys; | |
| fetch_keys_and_reset(reporter, handler, precompileContext, | |
| &recreatedKeys, &recreatedAndroidStyleKeys, /* reset= */ false); | |
| REPORTER_ASSERT(reporter, recreatedKeys.size() == 1); | |
| REPORTER_ASSERT(reporter, origKeys[0] == recreatedKeys[0]); | |
| REPORTER_ASSERT(reporter, recreatedAndroidStyleKeys.size() == 1); | |
| REPORTER_ASSERT(reporter, androidStyleKeys[0]->equals(recreatedAndroidStyleKeys[0].get())); | |
| int numBeforeSecondDraw = globalCache->numGraphicsPipelines(); | |
| draw_with_normal_api(context, recorder, paint); | |
| // None of the user-defined stable runtime effects should've been transmuted to not-stable | |
| REPORTER_ASSERT(reporter, !shaderCodeDictionary->numUserDefinedRuntimeEffects()); | |
| // Re-drawing shouldn't create any new pipelines | |
| REPORTER_ASSERT(reporter, numBeforeSecondDraw == globalCache->numGraphicsPipelines(), | |
| "%d != %d", numBeforeSecondDraw, globalCache->numGraphicsPipelines()); | |
| } | |
| // Get the existing keys, reset, and then try recreating them all using the normal precompile API | |
| void reset_and_recreate_pipelines_with_normal_precompile_api( | |
| skiatest::Reporter* reporter, | |
| Context* context, | |
| Recorder* recorder, | |
| PrecompileContext* precompileContext, | |
| PipelineCallBackHandler* handler) { | |
| // We don't attach runtime blenders to the SkPaint and PaintOptions in this case bc that will | |
| // force a dest read and complicate the normal-pipeline/Precompile-pipeline | |
| // comparison on Native Mac and Vulkan. This is bc, for those platforms, Precompile skips some | |
| // LoadOp combinations which don't matter for those platforms (please see 'numLoadOps' in | |
| // Precompile) but are serialized (for simplicity) in the serialized pipeline keys. | |
| auto [paint, paintOptions] = create_paint_and_options(/* addBlenders= */ false); | |
| draw_with_normal_api(context, recorder, paint); | |
| std::vector<skgpu::UniqueKey> origKeys; | |
| std::vector<sk_sp<SkData>> androidStyleKeys; | |
| fetch_keys_and_reset(reporter, handler, precompileContext, | |
| &origKeys, &androidStyleKeys, /* reset= */ true); | |
| // Given 'draw_with_normal_api' we expect one serialized key - full of user-defined stable keys | |
| REPORTER_ASSERT(reporter, origKeys.size() == 1); | |
| REPORTER_ASSERT(reporter, androidStyleKeys.size() == 1); | |
| RenderPassProperties renderPassProps; | |
| renderPassProps.fDSFlags = gGraphiteAvoidDepth ? DepthStencilFlags::kNone : DepthStencilFlags::kDepth; | |
| Precompile(precompileContext, | |
| paintOptions, | |
| DrawTypeFlags::kSimpleShape, | |
| {{ renderPassProps }}); | |
| // We need to explicitly wait for the precompilation to finish here | |
| context->priv().sharedContext()->pipelineManager()->wait_TestOnly(); | |
| std::vector<skgpu::UniqueKey> recreatedKeys; | |
| std::vector<sk_sp<SkData>> recreatedAndroidStyleKeys; | |
| fetch_keys_and_reset(reporter, handler, precompileContext, | |
| &recreatedKeys, &recreatedAndroidStyleKeys, /* reset= */ true); | |
| // The normal precompile API will overgenerate, so we need to search for a match | |
| { | |
| bool foundIt = false; | |
| for (const skgpu::UniqueKey& k : recreatedKeys) { | |
| if (origKeys[0] == k) { | |
| foundIt = true; | |
| break; | |
| } | |
| } | |
| REPORTER_ASSERT(reporter, foundIt); | |
| } | |
| { | |
| bool foundIt = false; | |
| for (const sk_sp<SkData>& k : recreatedAndroidStyleKeys) { | |
| if (androidStyleKeys[0]->equals(k.get())) { | |
| foundIt = true; | |
| break; | |
| } | |
| } | |
| REPORTER_ASSERT(reporter, foundIt); | |
| } | |
| } | |
| // This helper creates a defective user-defined known runtime effect pair: | |
| // the blessed shader will have a user-defined stable key | |
| // the cursed shader has the same SkSL as the blessed one but no stable key | |
| std::pair<sk_sp<SkShader>, sk_sp<SkShader>> make_defective_annulus_shader_pair() { | |
| SkRuntimeEffect* blessed = PrecompileFactories::GetAnnulusShaderEffect(); | |
| sk_sp<SkRuntimeEffect> cursed(SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader, | |
| PrecompileFactories::GetAnnulusShaderCode())); | |
| SkASSERT(blessed != cursed.get()); | |
| static const float kUniforms[4] = { 50.0f, 50.0f, 40.0f, 50.0f }; | |
| sk_sp<SkData> uniforms = SkData::MakeWithCopy(kUniforms, sizeof(kUniforms)); | |
| return { blessed->makeShader(uniforms, /* children= */ {}), | |
| cursed->makeShader(uniforms, /* children= */ {}) }; | |
| } | |
| // Draw once with a registered runtime effect, reset, and then re-draw w/ an un-registered | |
| // runtime effect that uses the same SkSL. | |
| void test_sksl_reuse(skiatest::Reporter* reporter, | |
| Context* context, | |
| Recorder* recorder, | |
| PrecompileContext* precompileContext, | |
| PipelineCallBackHandler* handler) { | |
| auto [blessedShader, cursedShader] = make_defective_annulus_shader_pair(); | |
| SkASSERT(blessedShader != cursedShader); | |
| // The blessed paint is the static one from the PrecompileFactories which has been | |
| // registered as a user-defined known runtime effect. | |
| SkPaint blessedPaint; | |
| blessedPaint.setShader(std::move(blessedShader)); | |
| // The cursed paint uses the same SkSL as the blessed version but uses a wholly separate | |
| // runtime effect (which is not registered). | |
| SkPaint cursedPaint; | |
| cursedPaint.setShader(std::move(cursedShader)); | |
| draw_with_normal_api(context, recorder, blessedPaint); | |
| std::vector<skgpu::UniqueKey> origKeys; | |
| std::vector<sk_sp<SkData>> serializedKeys; | |
| fetch_keys_and_reset(reporter, handler, precompileContext, | |
| &origKeys, &serializedKeys, /* reset= */ true); | |
| // Given 'draw_with_normal_api' we expect one serialized key - full of user-defined stable keys | |
| REPORTER_ASSERT(reporter, origKeys.size() == 1); | |
| REPORTER_ASSERT(reporter, serializedKeys.size() == 1); | |
| draw_with_normal_api(context, recorder, cursedPaint); | |
| std::vector<skgpu::UniqueKey> recreatedKeys; | |
| std::vector<sk_sp<SkData>> recreatedSerializedKeys; | |
| fetch_keys_and_reset(reporter, handler, precompileContext, | |
| &recreatedKeys, &recreatedSerializedKeys, /* reset= */ true); | |
| REPORTER_ASSERT(reporter, recreatedKeys.size() == 1); | |
| REPORTER_ASSERT(reporter, origKeys[0] == recreatedKeys[0]); | |
| // The un-registered runtime effect should've been mapped back to the registered one | |
| // and successfully serialized. | |
| REPORTER_ASSERT(reporter, recreatedSerializedKeys.size() == 1); | |
| REPORTER_ASSERT(reporter, serializedKeys[0]->equals(recreatedSerializedKeys[0].get())); | |
| } | |
| void test_get_pipeline_label_api(skiatest::Reporter* reporter, | |
| Context* context, | |
| Recorder* recorder, | |
| PrecompileContext* precompileContext, | |
| PipelineCallBackHandler* handler) { | |
| auto [paint, paintOptions] = create_paint_and_options(/* addBlenders= */ true); | |
| draw_with_normal_api(context, recorder, paint); | |
| std::vector<skgpu::UniqueKey> origKeys; | |
| std::vector<sk_sp<SkData>> androidStyleKeys; | |
| fetch_keys_and_reset(reporter, handler, precompileContext, | |
| &origKeys, &androidStyleKeys, /* reset= */ true); | |
| // Given 'draw_with_normal_api' we expect one serialized key - full of user-defined stable keys | |
| REPORTER_ASSERT(reporter, origKeys.size() == 1); | |
| REPORTER_ASSERT(reporter, androidStyleKeys.size() == 1); | |
| std::string label = precompileContext->getPipelineLabel(androidStyleKeys[0]); | |
| REPORTER_ASSERT(reporter, std::string::npos != label.find("AnnulusShader")); | |
| REPORTER_ASSERT(reporter, std::string::npos != label.find("SrcBlender")); | |
| REPORTER_ASSERT(reporter, std::string::npos != label.find("DstBlender")); | |
| REPORTER_ASSERT(reporter, std::string::npos != label.find("ComboBlender")); | |
| REPORTER_ASSERT(reporter, std::string::npos != label.find("DoubleColorFilter")); | |
| REPORTER_ASSERT(reporter, std::string::npos != label.find("ComboColorFilter")); | |
| // We withheld the HalfColorFilter name to test the default name case | |
| REPORTER_ASSERT(reporter, std::string::npos == label.find("HalfColorFilter")); | |
| REPORTER_ASSERT(reporter, std::string::npos != label.find("UserDefinedKnownRuntimeEffect")); | |
| } | |
| } // anonymous namespace | |
| // This test adds some user-defined stably-keyed runtime effects and then verifies that | |
| // everything behaves as expected. For the purposes of this test, "behaves as expected" means: | |
| // 1) the user-defined stably keyed runtime effects appear as such in the ShaderCodeDictionary | |
| // 2) the user-defined stable keys appear in the serialized Pipeline | |
| // keys (i.e., from the PipelineCallBackHandler) | |
| // 3) said keys correctly (re)generate the desired pipelines | |
| // 4) the normal (non-serialized-key) Precompile API generates the same keys. | |
| // 5) if the blessed stable runtime-effects aren't used, the free-range runtime-effects are | |
| // mapped back to the stable runtime-effects (clients really shouldn't do this though!) | |
| DEF_CONDITIONAL_GRAPHITE_TEST_FOR_CONTEXTS(UserDefinedStableKeyTest, | |
| skgpu::IsRenderingContext, | |
| reporter, | |
| origContext, | |
| origTestContext, | |
| origOptions, | |
| /* optionsProc= */ nullptr, | |
| /* condition= */ true, | |
| CtsEnforcement::kNever) { | |
| auto pipelineHandler = std::make_unique<PipelineCallBackHandler>(); | |
| TestOptions newOptions(origOptions); | |
| newOptions.fContextOptions.fPipelineCallbackContext = pipelineHandler.get(); | |
| newOptions.fContextOptions.fPipelineCachingCallback = PipelineCallBackHandler::CallBack; | |
| // We're going to also use all these runtime effects via the normal API | |
| // (c.f. create_paint_and_options) | |
| static const int kNumUserDefinedStableKeys = 7; | |
| sk_sp<SkRuntimeEffect> userDefinedKnownRuntimeEffects[kNumUserDefinedStableKeys] = { | |
| sk_ref_sp(PrecompileFactories::GetAnnulusShaderEffect()), | |
| sk_ref_sp(PrecompileFactories::GetSrcBlenderEffect()), | |
| sk_ref_sp(PrecompileFactories::GetDstBlenderEffect()), | |
| sk_ref_sp(PrecompileFactories::GetComboBlenderEffect()), | |
| sk_ref_sp(PrecompileFactories::GetDoubleColorFilterEffect()), | |
| sk_ref_sp(PrecompileFactories::GetHalfColorFilterEffect()), | |
| sk_ref_sp(PrecompileFactories::GetComboColorFilterEffect()), | |
| }; | |
| for (const sk_sp<SkRuntimeEffect>& e: userDefinedKnownRuntimeEffects) { | |
| // The PrecompileFactories runtime effects are static so prior runs may have already | |
| // set their StableKeys. Reset the StableKeys so all our expectations/asserts will be met. | |
| SkRuntimeEffectPriv::ResetStableKey(e.get()); | |
| } | |
| newOptions.fContextOptions.fUserDefinedKnownRuntimeEffects = { userDefinedKnownRuntimeEffects }; | |
| ContextFactory workaroundFactory(newOptions); | |
| ContextInfo ctxInfo = workaroundFactory.getContextInfo(origTestContext->contextType()); | |
| Context* newContext = ctxInfo.fContext; | |
| std::unique_ptr<PrecompileContext> precompileContext = newContext->makePrecompileContext(); | |
| GlobalCache* globalCache = precompileContext->priv().globalCache(); | |
| ShaderCodeDictionary* shaderCodeDictionary = newContext->priv().shaderCodeDictionary(); | |
| std::unique_ptr<Recorder> recorder = | |
| newContext->makeRecorder(ToolUtils::CreateTestingRecorderOptions()); | |
| REPORTER_ASSERT(reporter, !globalCache->numGraphicsPipelines()); | |
| // The next two lines check #1 above | |
| REPORTER_ASSERT(reporter, !shaderCodeDictionary->numUserDefinedRuntimeEffects()); | |
| REPORTER_ASSERT(reporter, shaderCodeDictionary->numUserDefinedKnownRuntimeEffects() == | |
| kNumUserDefinedStableKeys); | |
| // This verifies #2 and #3 above | |
| reset_and_recreate_pipelines_with_serialized_keys(reporter, | |
| newContext, | |
| recorder.get(), | |
| precompileContext.get(), | |
| pipelineHandler.get()); | |
| globalCache->resetGraphicsPipelines(); | |
| pipelineHandler->reset(); | |
| // This tests out #4 above | |
| reset_and_recreate_pipelines_with_normal_precompile_api(reporter, | |
| newContext, | |
| recorder.get(), | |
| precompileContext.get(), | |
| pipelineHandler.get()); | |
| globalCache->resetGraphicsPipelines(); | |
| pipelineHandler->reset(); | |
| // This tests out #5 above | |
| test_sksl_reuse(reporter, | |
| newContext, | |
| recorder.get(), | |
| precompileContext.get(), | |
| pipelineHandler.get()); | |
| globalCache->resetGraphicsPipelines(); | |
| pipelineHandler->reset(); | |
| // Extra little test to check on the getPipelineLabel API | |
| test_get_pipeline_label_api(reporter, | |
| newContext, | |
| recorder.get(), | |
| precompileContext.get(), | |
| pipelineHandler.get()); | |
| } | |
| // Test that the ShaderCodeDictionary can deduplicate the user-defined known runtime effect list | |
| DEF_CONDITIONAL_GRAPHITE_TEST_FOR_CONTEXTS(UserDefinedStableKeyTest_Duplicates, | |
| skgpu::IsRenderingContext, | |
| reporter, | |
| context, | |
| testContext, | |
| origOptions, | |
| /* optionsProc= */ nullptr, | |
| /* condition= */ true, | |
| CtsEnforcement::kNever) { | |
| auto pipelineHandler = std::make_unique<PipelineCallBackHandler>(); | |
| TestOptions newOptions(origOptions); | |
| newOptions.fContextOptions.fPipelineCallbackContext = pipelineHandler.get(); | |
| newOptions.fContextOptions.fPipelineCachingCallback = PipelineCallBackHandler::CallBack; | |
| sk_sp<SkRuntimeEffect> userDefinedKnownRuntimeEffects[] = { | |
| sk_ref_sp(PrecompileFactories::GetAnnulusShaderEffect()), | |
| sk_ref_sp(PrecompileFactories::GetAnnulusShaderEffect()), | |
| }; | |
| for (const sk_sp<SkRuntimeEffect>& e: userDefinedKnownRuntimeEffects) { | |
| // The PrecompileFactories runtime effects are static so prior runs may have already | |
| // set their StableKeys. Reset the StableKeys so all our expectations/asserts will be met. | |
| SkRuntimeEffectPriv::ResetStableKey(e.get()); | |
| } | |
| newOptions.fContextOptions.fUserDefinedKnownRuntimeEffects = { userDefinedKnownRuntimeEffects }; | |
| ContextFactory workaroundFactory(newOptions); | |
| ContextInfo ctxInfo = workaroundFactory.getContextInfo(testContext->contextType()); | |
| ShaderCodeDictionary* shaderCodeDictionary = ctxInfo.fContext->priv().shaderCodeDictionary(); | |
| REPORTER_ASSERT(reporter, shaderCodeDictionary->numUserDefinedKnownRuntimeEffects() == 1); | |
| } | |
| // Test that the ShaderCodeDictionary can handle nullptrs in the | |
| // user-defined known runtime effect list | |
| DEF_CONDITIONAL_GRAPHITE_TEST_FOR_CONTEXTS(UserDefinedStableKeyTest_Nullptrs, | |
| skgpu::IsRenderingContext, | |
| reporter, | |
| context, | |
| testContext, | |
| origOptions, | |
| /* optionsProc= */ nullptr, | |
| /* condition= */ true, | |
| CtsEnforcement::kNever) { | |
| auto pipelineHandler = std::make_unique<PipelineCallBackHandler>(); | |
| TestOptions newOptions(origOptions); | |
| newOptions.fContextOptions.fPipelineCallbackContext = pipelineHandler.get(); | |
| newOptions.fContextOptions.fPipelineCachingCallback = PipelineCallBackHandler::CallBack; | |
| sk_sp<SkRuntimeEffect> userDefinedKnownRuntimeEffects[] = { | |
| sk_ref_sp(PrecompileFactories::GetAnnulusShaderEffect()), | |
| nullptr, | |
| sk_ref_sp(PrecompileFactories::GetSrcBlenderEffect()), | |
| }; | |
| for (const sk_sp<SkRuntimeEffect>& e : userDefinedKnownRuntimeEffects) { | |
| // The PrecompileFactories runtime effects are static so prior runs may have already | |
| // set their StableKeys. Reset the StableKeys so all our expectations/asserts will be met. | |
| if (e) { | |
| SkRuntimeEffectPriv::ResetStableKey(e.get()); | |
| } | |
| } | |
| newOptions.fContextOptions.fUserDefinedKnownRuntimeEffects = { userDefinedKnownRuntimeEffects }; | |
| ContextFactory workaroundFactory(newOptions); | |
| ContextInfo ctxInfo = workaroundFactory.getContextInfo(testContext->contextType()); | |
| ShaderCodeDictionary* shaderCodeDictionary = ctxInfo.fContext->priv().shaderCodeDictionary(); | |
| REPORTER_ASSERT(reporter, shaderCodeDictionary->numUserDefinedKnownRuntimeEffects() == 2); | |
| } | |
| // Test that the ShaderCodeDictionary can handle excess user-defined known runtime effects | |
| DEF_CONDITIONAL_GRAPHITE_TEST_FOR_CONTEXTS(UserDefinedStableKeyTest_Overflow, | |
| skgpu::IsRenderingContext, | |
| reporter, | |
| context, | |
| testContext, | |
| origOptions, | |
| /* optionsProc= */ nullptr, | |
| /* condition= */ true, | |
| CtsEnforcement::kNever) { | |
| auto pipelineHandler = std::make_unique<PipelineCallBackHandler>(); | |
| TestOptions newOptions(origOptions); | |
| newOptions.fContextOptions.fPipelineCallbackContext = pipelineHandler.get(); | |
| newOptions.fContextOptions.fPipelineCachingCallback = PipelineCallBackHandler::CallBack; | |
| std::vector<sk_sp<SkRuntimeEffect>> userDefinedKnownRuntimeEffects; | |
| for (int i = 0; i < 2*SkKnownRuntimeEffects::kUserDefinedKnownRuntimeEffectsReservedCnt; ++i) { | |
| SkString sksl; | |
| sksl.printf("half4 main(float2 xy) { return half4(%d/255.0, %d/255.0, %d/255.0, 1.0); }", | |
| i, i, i); | |
| userDefinedKnownRuntimeEffects.push_back(SkRuntimeEffect::MakeForShader(sksl).effect); | |
| } | |
| newOptions.fContextOptions.fUserDefinedKnownRuntimeEffects = { userDefinedKnownRuntimeEffects }; | |
| ContextFactory workaroundFactory(newOptions); | |
| ContextInfo ctxInfo = workaroundFactory.getContextInfo(testContext->contextType()); | |
| ShaderCodeDictionary* shaderCodeDictionary = ctxInfo.fContext->priv().shaderCodeDictionary(); | |
| REPORTER_ASSERT(reporter, shaderCodeDictionary->numUserDefinedKnownRuntimeEffects() == | |
| SkKnownRuntimeEffects::kUserDefinedKnownRuntimeEffectsReservedCnt); | |
| } | |
| #endif // SK_GRAPHITE |