| /* |
| * Copyright 2026 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/SkExecutor.h" |
| #include "include/gpu/graphite/Context.h" |
| #include "include/gpu/graphite/ContextOptions.h" |
| #include "include/gpu/graphite/GraphiteTypes.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 "src/gpu/graphite/ContextPriv.h" |
| #include "tools/graphite/GraphiteTestContext.h" |
| #include "tools/graphite/TestOptions.h" |
| |
| using namespace::skgpu::graphite; |
| using namespace skiatest::graphite; |
| |
| const RenderPassProperties kRGBA_1_D { |
| DepthStencilFlags::kDepth, |
| kRGBA_8888_SkColorType, |
| /* fDstCS= */ nullptr, |
| /* fRequiresMSAA= */ false |
| }; |
| |
| namespace { |
| |
| void precompile_an_arc_draw(skiatest::Reporter* reporter, |
| PrecompileContext* precompileContext) { |
| PaintOptions paintOptions; |
| paintOptions.addBlendMode(SkBlendMode::kSrcOver); |
| |
| Precompile(precompileContext, |
| paintOptions, |
| DrawTypeFlags::kCircularArc, |
| { kRGBA_1_D }); |
| } |
| |
| void draw_an_arc(skiatest::Reporter* reporter, Context* context) { |
| std::unique_ptr<Recorder> recorder = context->makeRecorder(); |
| |
| SkImageInfo ii = SkImageInfo::Make(16, 16, |
| kRGBA_8888_SkColorType, |
| kPremul_SkAlphaType); |
| |
| |
| sk_sp<SkSurface> surf = SkSurfaces::RenderTarget(recorder.get(), ii, skgpu::Mipmapped::kNo); |
| SkCanvas* canvas = surf->getCanvas(); |
| |
| SkArc arc = SkArc::Make(SkRect::MakeWH(16, 16), 0, 270, SkArc::Type::kWedge); |
| SkPaint paint; |
| paint.setBlendMode(SkBlendMode::kSrcOver); |
| |
| canvas->drawArc(arc, paint); |
| |
| std::unique_ptr<Recording> recording = recorder->snap(); |
| if (!recording) { |
| ERRORF(reporter, "Failed to make recording"); |
| return; |
| } |
| |
| context->insertRecording({ recording.get() }); |
| } |
| |
| void run_test(skiatest::Reporter* reporter, GraphiteTestContext* testContext, |
| const TestOptions& origOptions, bool allowThreads, bool precompileFirst) { |
| std::unique_ptr<SkExecutor> executor; |
| if (allowThreads) { |
| // Ensure the threaded PipelineManager will be used |
| executor = SkExecutor::MakeMultiListFIFOThreadPool(/* numWorkLists= */ 2, |
| /* threads= */ 1, |
| /* allowBorrowing= */ false); |
| } |
| |
| TestOptions newOptions(origOptions); |
| newOptions.fContextOptions.fExecutor = executor.get(); |
| |
| { |
| std::unique_ptr<Context> context = testContext->makeContext(newOptions); |
| std::unique_ptr<PrecompileContext> precompileContext = context->makePrecompileContext(); |
| |
| if (precompileFirst) { |
| precompile_an_arc_draw(reporter, precompileContext.get()); |
| } |
| |
| draw_an_arc(reporter, context.get()); |
| |
| if (!precompileFirst) { |
| precompile_an_arc_draw(reporter, precompileContext.get()); |
| } |
| |
| testContext->syncedSubmit(context.get()); |
| |
| const GlobalCache::PipelineStats stats = context->priv().globalCache()->getStats(); |
| const PipelineManager::Stats& mgrStats = |
| context->priv().sharedContext()->pipelineManager()->getStats(); |
| |
| REPORTER_ASSERT(reporter, stats.fGraphicsCacheAdditions == 1); |
| REPORTER_ASSERT(reporter, stats.fUnpreemptedPrecompilePipelines == |
| (precompileFirst ? 1 : 0)); |
| REPORTER_ASSERT(reporter, mgrStats.fNumTasksCreated == 1); |
| } |
| } |
| |
| } // anonymous namespace |
| |
| // The goal here is to test out the PipelineManager's de-duplication of Pipeline |
| // creation tasks for both threaded and non-threaded PipelineManagers. |
| // The tests will request the same Pipeline via both Precompilation and drawing - |
| // altering which one comes first. |
| |
| |
| // These two tests forcibly create a threaded PipelineManager and then request the |
| // same Pipeline. |
| // If precompilation is first the draw should use the precompiled Pipeline. |
| // If the draw is first the precompilation should just find it. |
| DEF_CONDITIONAL_GRAPHITE_TEST_FOR_CONTEXTS(PipelineManagerThreadedTest_1, |
| skgpu::IsRenderingContext, |
| reporter, |
| context, |
| origTestContext, |
| origOptions, |
| /* optionsProc= */ nullptr, |
| /* condition= */ true, |
| CtsEnforcement::kNever) { |
| run_test(reporter, origTestContext, origOptions, |
| /* allowThreads= */ true, /* precompileFirst=*/ true); |
| } |
| |
| DEF_CONDITIONAL_GRAPHITE_TEST_FOR_CONTEXTS(PipelineManagerThreadedTest_2, |
| skgpu::IsRenderingContext, |
| reporter, |
| context, |
| origTestContext, |
| origOptions, |
| /* optionsProc= */ nullptr, |
| /* condition= */ true, |
| CtsEnforcement::kNever) { |
| run_test(reporter, origTestContext, origOptions, |
| /* allowThreads= */ true, /* precompileFirst=*/ false); |
| } |
| |
| // The next two tests are the same as above but deliberately create a non-threaded |
| // PipelineManager. |
| DEF_CONDITIONAL_GRAPHITE_TEST_FOR_CONTEXTS(PipelineManagerThreadedTest_3, |
| skgpu::IsRenderingContext, |
| reporter, |
| context, |
| origTestContext, |
| origOptions, |
| /* optionsProc= */ nullptr, |
| /* condition= */ true, |
| CtsEnforcement::kNever) { |
| run_test(reporter, origTestContext, origOptions, |
| /* allowThreads= */ false, /* precompileFirst=*/ true); |
| } |
| |
| DEF_CONDITIONAL_GRAPHITE_TEST_FOR_CONTEXTS(PipelineManagerThreadedTest_4, |
| skgpu::IsRenderingContext, |
| reporter, |
| context, |
| origTestContext, |
| origOptions, |
| /* optionsProc= */ nullptr, |
| /* condition= */ true, |
| CtsEnforcement::kNever) { |
| run_test(reporter, origTestContext, origOptions, |
| /* allowThreads= */ false, /* precompileFirst=*/ false); |
| } |
| #endif // SK_GRAPHITE |