| /* |
| * 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" |
| |
| #include "include/core/SkCanvas.h" |
| #include "include/core/SkPaint.h" |
| #include "include/core/SkString.h" |
| #include "include/core/SkSurface.h" |
| #include "include/core/SkTraceMemoryDump.h" |
| #include "include/gpu/graphite/Context.h" |
| #include "include/gpu/graphite/Recorder.h" |
| #include "include/gpu/graphite/Recording.h" |
| #include "include/gpu/graphite/Surface.h" |
| #include "src/gpu/graphite/Caps.h" |
| #include "src/gpu/graphite/ContextPriv.h" |
| #include "src/gpu/graphite/ContextUtils.h" |
| #include "src/gpu/graphite/Renderer.h" |
| #include "src/gpu/graphite/Surface_Graphite.h" |
| #include "src/gpu/graphite/TextureFormat.h" |
| #include "src/gpu/graphite/TextureInfoPriv.h" |
| |
| namespace skgpu::graphite { |
| |
| /** |
| * Implementation of SkTraceMemoryDump which tracks the number of resources whose type values |
| * contain a provided string within a ResourceCache. Could easily be elevated to be shared if other |
| * tests could benefit from this utility, but for now is only used within this test. |
| */ |
| class ResourceTypeQuantifier : public SkTraceMemoryDump { |
| public: |
| ResourceTypeQuantifier() = delete; |
| ~ResourceTypeQuantifier() override {} |
| ResourceTypeQuantifier(SkString resourceTypeSubstr) : fResourceTypeSubstr(resourceTypeSubstr) {} |
| |
| // Override relevant methods |
| void dumpStringValue(const char* dumpName, const char* valueName, const char* value) override { |
| if (SkString("type").equals(valueName)) { |
| if (SkString(value).contains(fResourceTypeSubstr.c_str())) { |
| fNumResourcesWithTypeSubstr++; |
| } |
| } |
| } |
| |
| LevelOfDetail getRequestedDetails() const override { |
| return SkTraceMemoryDump::kObjectsBreakdowns_LevelOfDetail; |
| } |
| |
| bool shouldDumpSizelessObjects() const override { return true; } |
| |
| // Define methods unique to this implementation |
| uint8_t numResourcesWithTypeSubstr() const { return fNumResourcesWithTypeSubstr; } |
| void resetCount() { fNumResourcesWithTypeSubstr = 0; } |
| |
| // Override unneeded methods to be no-ops |
| void setMemoryBacking(const char* dumpName, const char* backingType, |
| const char* backingObjectId) override {} |
| void setDiscardableMemoryBacking( |
| const char* dumpName, const SkDiscardableMemory& discardableMemoryObject) override {} |
| void dumpNumericValue(const char* dumpName, const char* valueName, const char* units, |
| uint64_t value) override {} |
| |
| private: |
| SkString fResourceTypeSubstr; |
| int fNumResourcesWithTypeSubstr = 0; |
| }; |
| |
| /** |
| * Unit test to ensure that the Vulkan backend shares renderpasses between draws that do and |
| * do not read from the dst texture as intended for optimization purposes. Could be expanded to test |
| * every blend mode that reads from the dst against all that do not, but testing just one should be |
| * sufficient to prove out that renderpasses can be shared between the different blend/draw types. |
| */ |
| DEF_GRAPHITE_TEST_FOR_VULKAN_CONTEXT(VulkanDstReadsShareRenderpass, reporter, context, |
| CtsEnforcement::kApiLevel_202504) { |
| // Set up a render target surface which we should be able to read from as an input attachment. |
| // Our Vulkan backend makes render targets usable as input attachments by default. |
| const Caps* caps = context->priv().caps(); |
| std::unique_ptr<Recorder> recorder = context->makeRecorder(); |
| SkImageInfo ii = SkImageInfo::Make(SkISize::Make(10, 10), |
| SkColorType::kRGBA_8888_SkColorType, |
| SkAlphaType::kPremul_SkAlphaType); |
| sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(recorder.get(), ii); |
| SkCanvas* canvas = surface->getCanvas(); |
| SharedContext* sharedContext = context->priv().sharedContext(); |
| |
| TextureFormat targetFormat = TextureInfoPriv::ViewFormat( |
| static_cast<const Surface*>(surface.get())->backingTextureProxy()->textureInfo()); |
| |
| // Select which blend modes to use for testing: one that we do expect to require a dst read on |
| // most hardware and one we do not. Assert that the dst read requirements of the modes align to |
| // expectations. For the sake of this test, assume Coverage::kNone. |
| static const SkBlendMode simpleBlendMode = SkBlendMode::kSrcATop; |
| static const SkBlendMode dstReadBlendMode = SkBlendMode::kLighten; |
| |
| REPORTER_ASSERT(reporter, |
| CanUseHardwareBlending(caps, targetFormat, simpleBlendMode, Coverage::kNone)); |
| // VK_EXT_blend_operation_advanced is supported on Nvidia hardware which does enable hardware |
| // blending for every Skia blend mode, so the following assertion is not universal. |
| // REPORTER_ASSERT(reporter, !CanUseHardwareBlending(caps, dstReadBlendMode, Coverage::kNone)); |
| |
| // Perform draw operations which do not read from the dst. Clear the canvas to initialize it to |
| // any color, drawing a color over it afterwards using the simple blend mode. |
| canvas->clear(SK_ColorMAGENTA); |
| canvas->drawColor(SK_ColorCYAN, simpleBlendMode); |
| std::unique_ptr<Recording> simpleBlendRecording = recorder->snap(); |
| |
| // Determine the number of renderpasses used for later comparison, asserting we have at least 1. |
| ResourceTypeQuantifier renderpassQuantifier { SkString("RenderPass") }; |
| sharedContext->dumpMemoryStatistics(&renderpassQuantifier); |
| const int numRenderPassesNoDstRead = renderpassQuantifier.numResourcesWithTypeSubstr(); |
| REPORTER_ASSERT(reporter, numRenderPassesNoDstRead > 0); |
| |
| // Perform a draw using a blend mode which does require reading from the dst texture. Determine |
| // how many renderpasses are in the cache after this operation. |
| canvas->drawColor(SK_ColorYELLOW, dstReadBlendMode); |
| std::unique_ptr<Recording> dstReadRecording = recorder->snap(); |
| renderpassQuantifier.resetCount(); |
| sharedContext->dumpMemoryStatistics(&renderpassQuantifier); |
| const int numRenderPassesAfterDstRead = renderpassQuantifier.numResourcesWithTypeSubstr(); |
| |
| // Assert that no new renderpasses have been added to the cache, meaning that one was reused |
| // from the draw that did not require a dst read. |
| REPORTER_ASSERT(reporter, |
| numRenderPassesNoDstRead == numRenderPassesAfterDstRead, |
| "# renderpasses with no dst read: %d ; after dst read: %d", |
| numRenderPassesNoDstRead, |
| numRenderPassesAfterDstRead); |
| } |
| |
| } // namespace skgpu::graphite |