| /* |
| * 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 "include/core/SkCanvas.h" |
| #include "include/core/SkImage.h" |
| #include "include/core/SkSurface.h" |
| #include "include/gpu/ganesh/GrRecordingContext.h" |
| #include "include/gpu/ganesh/SkSurfaceGanesh.h" |
| #include "include/private/chromium/GrDeferredDisplayListRecorder.h" |
| #include "include/private/chromium/GrSurfaceCharacterization.h" |
| #include "src/gpu/ResourceKey.h" |
| #include "src/gpu/ganesh/GrProxyProvider.h" |
| #include "src/gpu/ganesh/GrRecordingContextPriv.h" |
| #include "src/gpu/ganesh/GrTextureProxy.h" |
| #include "tests/CtsEnforcement.h" |
| #include "tests/Test.h" |
| #include "tools/ganesh/ProxyUtils.h" |
| |
| #include <thread> |
| |
| namespace { |
| |
| skgpu::UniqueKey create_key(int keyValue) { |
| static skgpu::UniqueKey::Domain d = skgpu::UniqueKey::GenerateDomain(); |
| |
| skgpu::UniqueKey key; |
| |
| skgpu::UniqueKey::Builder builder(&key, d, 1, nullptr); |
| builder[0] = keyValue; |
| builder.finish(); |
| |
| return key; |
| } |
| |
| sk_sp<SkImage> create_keyed_image(GrRecordingContext* rContext, int keyValue) { |
| SkImageInfo ii = SkImageInfo::MakeN32Premul(16, 16); |
| sk_sp<SkSurface> surf = SkSurfaces::RenderTarget(rContext, skgpu::Budgeted::kYes, ii); |
| |
| surf->getCanvas()->clear(SK_ColorWHITE); |
| surf->getCanvas()->drawCircle(8, 8, 8, SkPaint()); |
| |
| sk_sp<SkImage> img = surf->makeImageSnapshot(); |
| |
| GrTextureProxy* proxy = sk_gpu_test::GetTextureImageProxy(img.get(), |
| surf->recordingContext()); |
| |
| skgpu::UniqueKey key = create_key(keyValue); |
| |
| rContext->priv().proxyProvider()->assignUniqueKeyToProxy(key, proxy); |
| |
| return img; |
| } |
| |
| void ddl_deleter(skiatest::Reporter* reporter, |
| sk_sp<GrUniquelyKeyedProxyRegistry> registry, |
| sk_sp<GrDeferredDisplayList> ddl, |
| int keyValue) { |
| const auto kSleepDuration = std::chrono::milliseconds(1); |
| |
| std::this_thread::sleep_for(kSleepDuration); |
| |
| skgpu::UniqueKey key = create_key(keyValue); |
| |
| REPORTER_ASSERT(reporter, registry->find(key)); |
| ddl.reset(); |
| REPORTER_ASSERT(reporter, !registry->find(key)); |
| } |
| |
| } // anonymous namespace |
| |
| DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(GrUniqueProxyTest, |
| reporter, |
| ctxInfo, |
| CtsEnforcement::kNever) { |
| GrDirectContext* dContext = ctxInfo.directContext(); |
| |
| GrSurfaceCharacterization characterization; |
| |
| // Grab a characterization |
| { |
| SkImageInfo ii = SkImageInfo::MakeN32Premul(16, 16); |
| sk_sp<SkSurface> surf = SkSurfaces::RenderTarget(dContext, skgpu::Budgeted::kNo, ii); |
| SkAssertResult(surf); |
| |
| SkAssertResult(surf->characterize(&characterization)); |
| } |
| |
| // Create a recorder |
| std::unique_ptr<GrDeferredDisplayListRecorder> recorder; |
| recorder = std::make_unique<GrDeferredDisplayListRecorder>(characterization); |
| |
| GrProxyProvider* proxyProvider = recorder->getCanvas()->recordingContext()->priv().proxyProvider(); |
| sk_sp<GrUniquelyKeyedProxyRegistry> registry = proxyProvider->uniquelyKeyedProxyRegistry(); |
| |
| std::vector<std::thread> threads; |
| |
| // Create DDLs and immediately hand them off to a thread for deletion. Each DDL will have |
| // a single uniquely keyed proxy. |
| for (int i = 0; i < 10; i++) { |
| SkCanvas* canvas = recorder->getCanvas(); |
| canvas->clear(SkColors::kRed); |
| |
| sk_sp<SkImage> img = create_keyed_image(canvas->recordingContext(), i); |
| |
| canvas->drawImage(img.get(), 0, 0); |
| |
| sk_sp<GrDeferredDisplayList> ddl = recorder->detach(); |
| |
| threads.push_back(std::thread(ddl_deleter, reporter, registry, std::move(ddl), i)); |
| } |
| |
| // Delete the recorder - hopefully before all the DDLs are deleted |
| recorder.reset(); |
| |
| for (auto& thread : threads) { |
| if (thread.joinable()) { |
| thread.join(); |
| } |
| } |
| |
| // All the texture proxies should be deleted and have deregistered their keys |
| REPORTER_ASSERT(reporter, registry->count() == 0); |
| REPORTER_ASSERT(reporter, registry->unique()); |
| } |