• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2022 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "tests/Test.h"
9 
10 #include "include/core/SkBitmap.h"
11 #include "include/core/SkCanvas.h"
12 #include "include/core/SkImage.h"
13 #include "include/core/SkSurface.h"
14 #include "include/gpu/graphite/Context.h"
15 #include "include/gpu/graphite/Recorder.h"
16 #include "include/gpu/graphite/Recording.h"
17 #include "src/core/SkCanvasPriv.h"
18 #include "src/gpu/graphite/Device.h"
19 #include "src/gpu/graphite/RecorderPriv.h"
20 #include "src/gpu/graphite/Resource.h"
21 #include "src/gpu/graphite/ResourceCache.h"
22 #include "src/gpu/graphite/ResourceProvider.h"
23 #include "src/gpu/graphite/SharedContext.h"
24 #include "src/gpu/graphite/Texture.h"
25 #include "src/gpu/graphite/TextureProxyView.h"
26 #include "src/image/SkImage_Base.h"
27 
28 namespace skgpu::graphite {
29 
30 class TestResource : public Resource {
31 public:
Make(const SharedContext * sharedContext,Ownership owned,skgpu::Budgeted budgeted,Shareable shareable)32     static sk_sp<TestResource> Make(const SharedContext* sharedContext,
33                                     Ownership owned,
34                                     skgpu::Budgeted budgeted,
35                                     Shareable shareable) {
36         auto resource = sk_sp<TestResource>(new TestResource(sharedContext, owned, budgeted));
37         if (!resource) {
38             return nullptr;
39         }
40 
41         GraphiteResourceKey key;
42         CreateKey(&key, shareable);
43 
44         resource->setKey(key);
45         return resource;
46     }
47 
CreateKey(GraphiteResourceKey * key,Shareable shareable)48     static void CreateKey(GraphiteResourceKey* key, Shareable shareable) {
49         // Internally we assert that we don't make the same key twice where the only difference is
50         // shareable vs non-shareable. That allows us to now have Shareable be part of the Key's
51         // key. So here we make two different resource types so the keys will be different.
52         static const ResourceType kType = GraphiteResourceKey::GenerateResourceType();
53         static const ResourceType kShareableType = GraphiteResourceKey::GenerateResourceType();
54         ResourceType type = shareable == Shareable::kNo ? kType : kShareableType;
55         GraphiteResourceKey::Builder(key, type, 0, shareable);
56     }
57 
58 private:
TestResource(const SharedContext * sharedContext,Ownership owned,skgpu::Budgeted budgeted)59     TestResource(const SharedContext* sharedContext, Ownership owned, skgpu::Budgeted budgeted)
60             : Resource(sharedContext, owned, budgeted) {}
61 
freeGpuData()62     void freeGpuData() override {}
63 };
64 
create_image_data(const SkImageInfo & info)65 static sk_sp<SkData> create_image_data(const SkImageInfo& info) {
66     const size_t rowBytes = info.minRowBytes();
67     sk_sp<SkData> data(SkData::MakeUninitialized(rowBytes * info.height()));
68     {
69         SkBitmap bm;
70         bm.installPixels(info, data->writable_data(), rowBytes);
71         SkCanvas canvas(bm);
72         canvas.clear(SK_ColorRED);
73     }
74     return data;
75 }
76 
DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTS(GraphiteBudgetedResourcesTest,reporter,context)77 DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTS(GraphiteBudgetedResourcesTest, reporter, context) {
78     std::unique_ptr<Recorder> recorder = context->makeRecorder();
79     ResourceProvider* resourceProvider = recorder->priv().resourceProvider();
80     ResourceCache* resourceCache = resourceProvider->resourceCache();
81     const SharedContext* sharedContext = resourceProvider->sharedContext();
82 
83     REPORTER_ASSERT(reporter, resourceCache->getResourceCount() == 0);
84     REPORTER_ASSERT(reporter, resourceCache->numFindableResources() == 0);
85 
86     // Test making a non budgeted, non shareable resource.
87     auto resource = TestResource::Make(
88             sharedContext, Ownership::kOwned, skgpu::Budgeted::kNo, Shareable::kNo);
89     if (!resource) {
90         ERRORF(reporter, "Failed to make TestResource");
91         return;
92     }
93     Resource* resourcePtr = resource.get();
94 
95     REPORTER_ASSERT(reporter, resource->budgeted() == skgpu::Budgeted::kNo);
96     resourceCache->insertResource(resourcePtr);
97     REPORTER_ASSERT(reporter, resourceCache->getResourceCount() == 1);
98     // Resource is not shareable and we have a ref on it. Thus it shouldn't ben findable in the
99     // cache.
100     REPORTER_ASSERT(reporter, resourceCache->numFindableResources() == 0);
101 
102     // When we reset our TestResource it should go back into the cache since it can be used as a
103     // scratch texture (since it is not shareable). At that point the budget should be changed to
104     // skgpu::Budgeted::kYes.
105     resource.reset();
106     resourceCache->forceProcessReturnedResources();
107     REPORTER_ASSERT(reporter, resourceCache->getResourceCount() == 1);
108     REPORTER_ASSERT(reporter, resourceCache->numFindableResources() == 1);
109     // Even though we reset our ref on the resource we still have the ptr to it and should be the
110     // resource in the cache. So in general this is dangerous it should be safe for this test to
111     // directly access the texture.
112     REPORTER_ASSERT(reporter, resourcePtr->budgeted() == skgpu::Budgeted::kYes);
113 
114     GraphiteResourceKey key;
115     TestResource::CreateKey(&key, Shareable::kNo);
116     Resource* resourcePtr2 = resourceCache->findAndRefResource(key, skgpu::Budgeted::kNo);
117     REPORTER_ASSERT(reporter, resourcePtr == resourcePtr2);
118     REPORTER_ASSERT(reporter, resourceCache->getResourceCount() == 1);
119     REPORTER_ASSERT(reporter, resourceCache->numFindableResources() == 0);
120     REPORTER_ASSERT(reporter, resourcePtr2->budgeted() == skgpu::Budgeted::kNo);
121     resourcePtr2->unref();
122     resourceCache->forceProcessReturnedResources();
123 
124     // Test making a non budgeted, non shareable resource.
125     resource = TestResource::Make(
126             sharedContext, Ownership::kOwned, skgpu::Budgeted::kYes, Shareable::kYes);
127     if (!resource) {
128         ERRORF(reporter, "Failed to make TestResource");
129         return;
130     }
131     resourcePtr = resource.get();
132     REPORTER_ASSERT(reporter, resource->budgeted() == skgpu::Budgeted::kYes);
133     resourceCache->insertResource(resourcePtr);
134     REPORTER_ASSERT(reporter, resourceCache->getResourceCount() == 2);
135     REPORTER_ASSERT(reporter, resourceCache->numFindableResources() == 2);
136 
137     resource.reset();
138     resourceCache->forceProcessReturnedResources();
139     REPORTER_ASSERT(reporter, resourceCache->getResourceCount() == 2);
140     REPORTER_ASSERT(reporter, resourceCache->numFindableResources() == 2);
141     REPORTER_ASSERT(reporter, resourcePtr->budgeted() == skgpu::Budgeted::kYes);
142 
143     TestResource::CreateKey(&key, Shareable::kYes);
144     resourcePtr2 = resourceCache->findAndRefResource(key, skgpu::Budgeted::kYes);
145     REPORTER_ASSERT(reporter, resourcePtr == resourcePtr2);
146     REPORTER_ASSERT(reporter, resourceCache->getResourceCount() == 2);
147     REPORTER_ASSERT(reporter, resourceCache->numFindableResources() == 2);
148     REPORTER_ASSERT(reporter, resourcePtr2->budgeted() == skgpu::Budgeted::kYes);
149     resourcePtr2->unref();
150 
151     ///////////////////////////////////////////////////////////////////////////////////////////////
152     // Test that SkImage's and SkSurface's underlying Resource's follow the expected budgeted
153     // system.
154     auto info = SkImageInfo::Make(10, 10, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
155 
156     // First test SkImages. Since we can't directly create a Graphite SkImage we first have to make
157     // a raster SkImage than convert that to a Graphite SkImage via makeTextureImage.
158     sk_sp<SkData> data(create_image_data(info));
159     sk_sp<SkImage> image = SkImage::MakeRasterData(info, std::move(data), info.minRowBytes());
160     REPORTER_ASSERT(reporter, image);
161 
162     sk_sp<SkImage> imageGpu = image->makeTextureImage(recorder.get());
163     REPORTER_ASSERT(reporter, imageGpu);
164 
165     TextureProxy* imageProxy = nullptr;
166     {
167         // We don't want the view holding a ref to the Proxy or else we can't send things back to
168         // the cache.
169         auto [view, colorType] = as_IB(imageGpu.get())->asView(recorder.get(), Mipmapped::kNo);
170         REPORTER_ASSERT(reporter, view);
171         imageProxy = view.proxy();
172     }
173     // Make sure the proxy is instantiated
174     if (!imageProxy->instantiate(resourceProvider)) {
175         ERRORF(reporter, "Failed to instantiate Proxy");
176         return;
177     }
178     const Resource* imageResourcePtr = imageProxy->texture();
179     REPORTER_ASSERT(reporter, imageResourcePtr);
180     // There is an extra resource for the buffer that is uploading the data to the texture
181     REPORTER_ASSERT(reporter, resourceCache->getResourceCount() == 4);
182     REPORTER_ASSERT(reporter, resourceCache->numFindableResources() == 2);
183     REPORTER_ASSERT(reporter, imageResourcePtr->budgeted() == skgpu::Budgeted::kNo);
184 
185     // Submit all upload work so we can drop refs to the image and get it returned to the cache.
186     std::unique_ptr<Recording> recording = recorder->snap();
187     if (!recording) {
188         ERRORF(reporter, "Failed to make recording");
189         return;
190     }
191     InsertRecordingInfo insertInfo;
192     insertInfo.fRecording = recording.get();
193     context->insertRecording(insertInfo);
194     context->submit(SyncToCpu::kYes);
195     recording.reset();
196     imageGpu.reset();
197     resourceCache->forceProcessReturnedResources();
198 
199     REPORTER_ASSERT(reporter, resourceCache->getResourceCount() == 4);
200     REPORTER_ASSERT(reporter, resourceCache->numFindableResources() == 4);
201     REPORTER_ASSERT(reporter, imageResourcePtr->budgeted() == skgpu::Budgeted::kYes);
202 
203     // Now try an SkSurface. This is simpler since we can directly create Graphite SkSurface's.
204     sk_sp<SkSurface> surface = SkSurface::MakeGraphite(recorder.get(), info);
205     if (!surface) {
206         ERRORF(reporter, "Failed to make surface");
207         return;
208     }
209 
210     TextureProxy* surfaceProxy = SkCanvasPriv::TopDeviceGraphiteTargetProxy(surface->getCanvas());
211     if (!surfaceProxy) {
212         ERRORF(reporter, "Failed to get surface proxy");
213         return;
214     }
215 
216     // Make sure the proxy is instantiated
217     if (!surfaceProxy->instantiate(resourceProvider)) {
218         ERRORF(reporter, "Failed to instantiate surface proxy");
219         return;
220     }
221     const Resource* surfaceResourcePtr = surfaceProxy->texture();
222 
223     REPORTER_ASSERT(reporter, resourceCache->getResourceCount() == 5);
224     REPORTER_ASSERT(reporter, resourceCache->numFindableResources() == 4);
225     REPORTER_ASSERT(reporter, surfaceResourcePtr->budgeted() == skgpu::Budgeted::kNo);
226 
227     // The creation of the surface may have added an initial clear to it. Thus if we just reset the
228     // surface it will flush the clean on the device and we don't be dropping all our refs to the
229     // surface. So we force all the work to happen first.
230     recording = recorder->snap();
231     insertInfo.fRecording = recording.get();
232     context->insertRecording(insertInfo);
233     context->submit(SyncToCpu::kYes);
234     recording.reset();
235 
236     surface.reset();
237     resourceCache->forceProcessReturnedResources();
238     REPORTER_ASSERT(reporter, surfaceResourcePtr->budgeted() == skgpu::Budgeted::kYes);
239 }
240 
241 }  // namespace skgpu::graphite
242