1/*
2 * Copyright 2017 Google Inc.
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// This is a GPU-backend specific test.
9
10#include "Test.h"
11
12#if SK_SUPPORT_GPU
13
14#include "GrBackendSurface.h"
15#include "GrContextPriv.h"
16#include "GrResourceCache.h"
17#include "GrProxyProvider.h"
18#include "GrResourceProvider.h"
19#include "GrTest.h"
20#include "GrTexture.h"
21#include "GrTextureProxy.h"
22
23#include "SkGr.h"
24#include "SkImage.h"
25
26int GrProxyProvider::numUniqueKeyProxies_TestOnly() const {
27    return fUniquelyKeyedProxies.count();
28}
29
30static GrSurfaceDesc make_desc(GrSurfaceFlags flags) {
31    GrSurfaceDesc desc;
32    desc.fFlags = flags;
33    desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
34    desc.fWidth = 64;
35    desc.fHeight = 64;
36    desc.fConfig = kRGBA_8888_GrPixelConfig;
37    desc.fSampleCnt = 1;
38
39    return desc;
40}
41
42///////////////////////////////////////////////////////////////////////////////////////////////////
43// Basic test
44
45static sk_sp<GrTextureProxy> deferred_tex(skiatest::Reporter* reporter,
46                                          GrProxyProvider* proxyProvider, SkBackingFit fit) {
47    const GrSurfaceDesc desc = make_desc(kNone_GrSurfaceFlags);
48
49    sk_sp<GrTextureProxy> proxy = proxyProvider->createProxy(desc, fit, SkBudgeted::kYes);
50    // Only budgeted & wrapped external proxies get to carry uniqueKeys
51    REPORTER_ASSERT(reporter, !proxy->getUniqueKey().isValid());
52    return proxy;
53}
54
55static sk_sp<GrTextureProxy> deferred_texRT(skiatest::Reporter* reporter,
56                                            GrProxyProvider* proxyProvider, SkBackingFit fit) {
57    const GrSurfaceDesc desc = make_desc(kRenderTarget_GrSurfaceFlag);
58
59    sk_sp<GrTextureProxy> proxy = proxyProvider->createProxy(desc, fit, SkBudgeted::kYes);
60    // Only budgeted & wrapped external proxies get to carry uniqueKeys
61    REPORTER_ASSERT(reporter, !proxy->getUniqueKey().isValid());
62    return proxy;
63}
64
65static sk_sp<GrTextureProxy> wrapped(skiatest::Reporter* reporter,
66                                     GrProxyProvider* proxyProvider, SkBackingFit fit) {
67    const GrSurfaceDesc desc = make_desc(kNone_GrSurfaceFlags);
68
69    sk_sp<GrTextureProxy> proxy = proxyProvider->createInstantiatedProxy(desc, fit,
70                                                                         SkBudgeted::kYes);
71    // Only budgeted & wrapped external proxies get to carry uniqueKeys
72    REPORTER_ASSERT(reporter, !proxy->getUniqueKey().isValid());
73    return proxy;
74}
75
76static sk_sp<GrTextureProxy> wrapped_with_key(skiatest::Reporter* reporter,
77                                              GrProxyProvider* proxyProvider, SkBackingFit fit) {
78    static GrUniqueKey::Domain d = GrUniqueKey::GenerateDomain();
79    static int kUniqueKeyData = 0;
80
81    GrUniqueKey key;
82
83    GrUniqueKey::Builder builder(&key, d, 1, nullptr);
84    builder[0] = kUniqueKeyData++;
85    builder.finish();
86
87    const GrSurfaceDesc desc = make_desc(kNone_GrSurfaceFlags);
88
89    // Only budgeted & wrapped external proxies get to carry uniqueKeys
90    sk_sp<GrTextureProxy> proxy = proxyProvider->createInstantiatedProxy(desc, fit,
91                                                                         SkBudgeted::kYes, 0);
92    SkAssertResult(proxyProvider->assignUniqueKeyToProxy(key, proxy.get()));
93    REPORTER_ASSERT(reporter, proxy->getUniqueKey().isValid());
94    return proxy;
95}
96
97static sk_sp<GrTextureProxy> create_wrapped_backend(GrContext* context, SkBackingFit fit,
98                                                    sk_sp<GrTexture>* backingSurface) {
99    GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
100    GrResourceProvider* resourceProvider = context->contextPriv().resourceProvider();
101
102    const GrSurfaceDesc desc = make_desc(kNone_GrSurfaceFlags);
103
104    *backingSurface = resourceProvider->createTexture(desc, SkBudgeted::kNo);
105    if (!(*backingSurface)) {
106        return nullptr;
107    }
108
109    GrBackendTexture backendTex = (*backingSurface)->getBackendTexture();
110
111    return proxyProvider->createWrappedTextureProxy(backendTex, kBottomLeft_GrSurfaceOrigin);
112}
113
114
115// This tests the basic capabilities of the uniquely keyed texture proxies. Does assigning
116// and looking them up work, etc.
117static void basic_test(GrContext* context,
118                       skiatest::Reporter* reporter,
119                       sk_sp<GrTextureProxy> proxy, bool proxyIsCached) {
120    static int id = 1;
121
122    GrResourceProvider* resourceProvider = context->contextPriv().resourceProvider();
123    GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
124    GrResourceCache* cache = context->contextPriv().getResourceCache();
125
126    int startCacheCount = cache->getResourceCount();
127
128    GrUniqueKey key;
129    if (proxy->getUniqueKey().isValid()) {
130        key = proxy->getUniqueKey();
131    } else {
132        GrMakeKeyFromImageID(&key, id, SkIRect::MakeWH(64, 64));
133        ++id;
134
135        // Assigning the uniqueKey adds the proxy to the hash but doesn't force instantiation
136        REPORTER_ASSERT(reporter, !proxyProvider->numUniqueKeyProxies_TestOnly());
137        SkAssertResult(proxyProvider->assignUniqueKeyToProxy(key, proxy.get()));
138    }
139
140    REPORTER_ASSERT(reporter, 1 == proxyProvider->numUniqueKeyProxies_TestOnly());
141    REPORTER_ASSERT(reporter, startCacheCount == cache->getResourceCount());
142
143    // setUniqueKey had better stick
144    REPORTER_ASSERT(reporter, key == proxy->getUniqueKey());
145
146    // We just added it, surely we can find it
147    REPORTER_ASSERT(reporter, proxyProvider->findOrCreateProxyByUniqueKey(
148                                                                key, kBottomLeft_GrSurfaceOrigin));
149    REPORTER_ASSERT(reporter, 1 == proxyProvider->numUniqueKeyProxies_TestOnly());
150
151    // Once instantiated, the backing resource should have the same key
152    SkAssertResult(proxy->instantiate(resourceProvider));
153    const GrUniqueKey& texKey = proxy->priv().peekSurface()->getUniqueKey();
154    REPORTER_ASSERT(reporter, texKey.isValid());
155    REPORTER_ASSERT(reporter, key == texKey);
156    if (proxyIsCached) {
157        REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
158    }
159
160    // deleting the proxy should delete it from the hash but not the cache
161    proxy = nullptr;
162    REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
163    REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
164
165    // If the proxy was cached refinding it should bring it back to life
166    proxy = proxyProvider->findOrCreateProxyByUniqueKey(key, kBottomLeft_GrSurfaceOrigin);
167    if (proxyIsCached) {
168        REPORTER_ASSERT(reporter, proxy);
169        REPORTER_ASSERT(reporter, 1 == proxyProvider->numUniqueKeyProxies_TestOnly());
170    } else {
171        REPORTER_ASSERT(reporter, !proxy);
172        REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
173    }
174    REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
175
176    // Mega-purging it should remove it from both the hash and the cache
177    proxy = nullptr;
178    cache->purgeAllUnlocked();
179    if (proxyIsCached) {
180        REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
181    } else {
182        REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
183    }
184
185    // We can bring neither the texture nor proxy back from perma-death
186    proxy = proxyProvider->findOrCreateProxyByUniqueKey(key, kBottomLeft_GrSurfaceOrigin);
187    REPORTER_ASSERT(reporter, !proxy);
188    if (proxyIsCached) {
189        REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
190    } else {
191        REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
192    }
193}
194
195///////////////////////////////////////////////////////////////////////////////////////////////////
196// Invalidation test
197
198// Test if invalidating unique ids operates as expected for texture proxies.
199static void invalidation_test(GrContext* context, skiatest::Reporter* reporter) {
200
201    GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
202    GrResourceCache* cache = context->contextPriv().getResourceCache();
203    REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
204
205    sk_sp<SkImage> rasterImg;
206
207    {
208        SkImageInfo ii = SkImageInfo::Make(64, 64, kRGBA_8888_SkColorType, kOpaque_SkAlphaType);
209
210        SkBitmap bm;
211        bm.allocPixels(ii);
212
213        rasterImg = SkImage::MakeFromBitmap(bm);
214        REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
215        REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
216    }
217
218    sk_sp<SkImage> textureImg = rasterImg->makeTextureImage(context, nullptr);
219    REPORTER_ASSERT(reporter, 1 == proxyProvider->numUniqueKeyProxies_TestOnly());
220    REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
221
222    rasterImg = nullptr;        // this invalidates the uniqueKey
223
224    // this forces the cache to respond to the inval msg
225    int maxNum;
226    size_t maxBytes;
227    context->getResourceCacheLimits(&maxNum, &maxBytes);
228    context->setResourceCacheLimits(maxNum-1, maxBytes);
229
230    REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
231    REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
232
233    textureImg = nullptr;
234    context->purgeAllUnlockedResources();
235
236    REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
237    REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
238}
239
240#ifndef SK_DISABLE_DEFERRED_PROXIES
241// Test if invalidating unique ids prior to instantiating operates as expected
242static void invalidation_and_instantiation_test(GrContext* context, skiatest::Reporter* reporter) {
243    GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
244    GrResourceProvider* resourceProvider = context->contextPriv().resourceProvider();
245    GrResourceCache* cache = context->contextPriv().getResourceCache();
246    REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
247
248    static GrUniqueKey::Domain d = GrUniqueKey::GenerateDomain();
249    GrUniqueKey key;
250    GrUniqueKey::Builder builder(&key, d, 1, nullptr);
251    builder[0] = 0;
252    builder.finish();
253
254    // Create proxy, assign unique key
255    sk_sp<GrTextureProxy> proxy = deferred_tex(reporter, proxyProvider, SkBackingFit::kExact);
256    SkAssertResult(proxyProvider->assignUniqueKeyToProxy(key, proxy.get()));
257
258    // Send an invalidation message, which will be sitting in the cache's inbox
259    SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(GrUniqueKeyInvalidatedMessage(key));
260
261    REPORTER_ASSERT(reporter, 1 == proxyProvider->numUniqueKeyProxies_TestOnly());
262    REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
263
264    // Instantiate the proxy. This will trigger the message to be processed, so the resulting
265    // texture should *not* have the unique key on it!
266    SkAssertResult(proxy->instantiate(resourceProvider));
267
268    REPORTER_ASSERT(reporter, !proxy->getUniqueKey().isValid());
269    REPORTER_ASSERT(reporter, !proxy->priv().peekTexture()->getUniqueKey().isValid());
270    REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
271    REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
272
273    proxy = nullptr;
274    context->purgeAllUnlockedResources();
275
276    REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
277    REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
278}
279#endif
280
281DEF_GPUTEST_FOR_RENDERING_CONTEXTS(TextureProxyTest, reporter, ctxInfo) {
282    GrContext* context = ctxInfo.grContext();
283    GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
284    GrResourceCache* cache = context->contextPriv().getResourceCache();
285
286    REPORTER_ASSERT(reporter, !proxyProvider->numUniqueKeyProxies_TestOnly());
287    REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
288
289    for (auto fit : { SkBackingFit::kExact, SkBackingFit::kApprox }) {
290        for (auto create : { deferred_tex, deferred_texRT, wrapped, wrapped_with_key }) {
291            REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
292            basic_test(context, reporter, create(reporter, proxyProvider, fit), true);
293        }
294
295        REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
296        sk_sp<GrTexture> backingTex;
297        sk_sp<GrTextureProxy> proxy = create_wrapped_backend(context, fit, &backingTex);
298        basic_test(context, reporter, std::move(proxy), false);
299
300        backingTex = nullptr;
301        cache->purgeAllUnlocked();
302    }
303
304    invalidation_test(context, reporter);
305#ifndef SK_DISABLE_DEFERRED_PROXIES
306    invalidation_and_instantiation_test(context, reporter);
307#endif
308}
309
310#endif
311