• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "include/core/SkTypes.h"
9 
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkPoint.h"
12 #include "include/core/SkSurface.h"
13 #include "include/gpu/GrBackendSurface.h"
14 #include "include/gpu/GrContext.h"
15 #include "src/gpu/GrBackendTextureImageGenerator.h"
16 #include "src/gpu/GrContextPriv.h"
17 #include "src/gpu/GrDrawingManager.h"
18 #include "src/gpu/GrGpu.h"
19 #include "src/gpu/GrRenderTargetContext.h"
20 #include "src/gpu/GrSemaphore.h"
21 #include "src/gpu/GrSurfaceProxyPriv.h"
22 #include "src/gpu/GrTexturePriv.h"
23 #include "src/gpu/GrTextureProxy.h"
24 #include "src/gpu/SkGpuDevice.h"
25 #include "src/image/SkImage_Base.h"
26 #include "src/image/SkSurface_Gpu.h"
27 #include "tests/Test.h"
28 
29 static constexpr int kSize = 8;
30 
31 // Test that the correct mip map states are on the GrTextures when wrapping GrBackendTextures in
32 // SkImages and SkSurfaces
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrWrappedMipMappedTest,reporter,ctxInfo)33 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrWrappedMipMappedTest, reporter, ctxInfo) {
34     GrContext* context = ctxInfo.grContext();
35     if (!context->priv().caps()->mipMapSupport()) {
36         return;
37     }
38 
39     for (auto mipMapped : {GrMipMapped::kNo, GrMipMapped::kYes}) {
40         for (auto renderable : {GrRenderable::kNo, GrRenderable::kYes}) {
41             // createBackendTexture currently doesn't support uploading data to mip maps
42             // so we don't send any. However, we pretend there is data for the checks below which is
43             // fine since we are never actually using these textures for any work on the gpu.
44             GrBackendTexture backendTex = context->createBackendTexture(
45                     kSize, kSize, kRGBA_8888_SkColorType,
46                     SkColors::kTransparent, mipMapped, renderable, GrProtected::kNo);
47 
48             sk_sp<GrTextureProxy> proxy;
49             sk_sp<SkImage> image;
50             if (GrRenderable::kYes == renderable) {
51                 sk_sp<SkSurface> surface = SkSurface::MakeFromBackendTexture(
52                                                                            context,
53                                                                            backendTex,
54                                                                            kTopLeft_GrSurfaceOrigin,
55                                                                            0,
56                                                                            kRGBA_8888_SkColorType,
57                                                                            nullptr,
58                                                                            nullptr);
59 
60                 SkGpuDevice* device = ((SkSurface_Gpu*)surface.get())->getDevice();
61                 proxy = device->accessRenderTargetContext()->asTextureProxyRef();
62             } else {
63                 image = SkImage::MakeFromTexture(context, backendTex,
64                                                  kTopLeft_GrSurfaceOrigin,
65                                                  kRGBA_8888_SkColorType,
66                                                  kPremul_SkAlphaType, nullptr,
67                                                  nullptr, nullptr);
68                 proxy = as_IB(image)->asTextureProxyRef(context);
69             }
70             REPORTER_ASSERT(reporter, proxy);
71             if (!proxy) {
72                 context->deleteBackendTexture(backendTex);
73                 return;
74             }
75 
76             REPORTER_ASSERT(reporter, proxy->isInstantiated());
77 
78             GrTexture* texture = proxy->peekTexture();
79             REPORTER_ASSERT(reporter, texture);
80             if (!texture) {
81                 context->deleteBackendTexture(backendTex);
82                 return;
83             }
84 
85             if (GrMipMapped::kYes == mipMapped) {
86                 REPORTER_ASSERT(reporter, GrMipMapped::kYes == texture->texturePriv().mipMapped());
87                 if (GrRenderable::kYes == renderable) {
88                     REPORTER_ASSERT(reporter, texture->texturePriv().mipMapsAreDirty());
89                 } else {
90                     REPORTER_ASSERT(reporter, !texture->texturePriv().mipMapsAreDirty());
91                 }
92             } else {
93                 REPORTER_ASSERT(reporter, GrMipMapped::kNo == texture->texturePriv().mipMapped());
94             }
95             context->deleteBackendTexture(backendTex);
96         }
97     }
98 }
99 
100 // Test that we correctly copy or don't copy GrBackendTextures in the GrBackendTextureImageGenerator
101 // based on if we will use mips in the draw and the mip status of the GrBackendTexture.
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrBackendTextureImageMipMappedTest,reporter,ctxInfo)102 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrBackendTextureImageMipMappedTest, reporter, ctxInfo) {
103     GrContext* context = ctxInfo.grContext();
104     if (!context->priv().caps()->mipMapSupport()) {
105         return;
106     }
107 
108     for (auto mipMapped : {GrMipMapped::kNo, GrMipMapped::kYes}) {
109         for (auto willUseMips : {false, true}) {
110             GrBackendTexture backendTex = context->createBackendTexture(
111                     kSize, kSize, kRGBA_8888_SkColorType,
112                     SkColors::kTransparent, mipMapped, GrRenderable::kNo, GrProtected::kNo);
113 
114             sk_sp<SkImage> image = SkImage::MakeFromTexture(context, backendTex,
115                                                             kTopLeft_GrSurfaceOrigin,
116                                                             kRGBA_8888_SkColorType,
117                                                             kPremul_SkAlphaType, nullptr,
118                                                             nullptr, nullptr);
119 
120             GrTextureProxy* proxy = as_IB(image)->peekProxy();
121             REPORTER_ASSERT(reporter, proxy);
122             if (!proxy) {
123                 context->deleteBackendTexture(backendTex);
124                 return;
125             }
126 
127             REPORTER_ASSERT(reporter, proxy->isInstantiated());
128 
129             sk_sp<GrTexture> texture = sk_ref_sp(proxy->peekTexture());
130             REPORTER_ASSERT(reporter, texture);
131             if (!texture) {
132                 context->deleteBackendTexture(backendTex);
133                 return;
134             }
135 
136             std::unique_ptr<SkImageGenerator> imageGen = GrBackendTextureImageGenerator::Make(
137                     texture, kTopLeft_GrSurfaceOrigin, nullptr, kRGBA_8888_SkColorType,
138                     kPremul_SkAlphaType, nullptr);
139             REPORTER_ASSERT(reporter, imageGen);
140             if (!imageGen) {
141                 context->deleteBackendTexture(backendTex);
142                 return;
143             }
144 
145             SkIPoint origin = SkIPoint::Make(0,0);
146             SkImageInfo imageInfo = SkImageInfo::Make(kSize, kSize, kRGBA_8888_SkColorType,
147                                                       kPremul_SkAlphaType);
148             sk_sp<GrTextureProxy> genProxy = imageGen->generateTexture(context, imageInfo,
149                                                                        origin, willUseMips);
150 
151             REPORTER_ASSERT(reporter, genProxy);
152             if (!genProxy) {
153                 context->deleteBackendTexture(backendTex);
154                 return;
155             }
156 
157             if (GrSurfaceProxy::LazyState::kNot != genProxy->lazyInstantiationState()) {
158                 genProxy->priv().doLazyInstantiation(context->priv().resourceProvider());
159             } else if (!genProxy->isInstantiated()) {
160                 genProxy->instantiate(context->priv().resourceProvider());
161             }
162 
163             REPORTER_ASSERT(reporter, genProxy->isInstantiated());
164             if (!genProxy->isInstantiated()) {
165                 context->deleteBackendTexture(backendTex);
166                 return;
167             }
168 
169             GrTexture* genTexture = genProxy->peekTexture();
170             REPORTER_ASSERT(reporter, genTexture);
171             if (!genTexture) {
172                 context->deleteBackendTexture(backendTex);
173                 return;
174             }
175 
176             GrBackendTexture genBackendTex = genTexture->getBackendTexture();
177 
178             if (GrBackendApi::kOpenGL == genBackendTex.backend()) {
179                 GrGLTextureInfo genTexInfo;
180                 GrGLTextureInfo origTexInfo;
181                 if (genBackendTex.getGLTextureInfo(&genTexInfo) &&
182                     backendTex.getGLTextureInfo(&origTexInfo)) {
183                     if (willUseMips && GrMipMapped::kNo == mipMapped) {
184                         // We did a copy so the texture IDs should be different
185                         REPORTER_ASSERT(reporter, origTexInfo.fID != genTexInfo.fID);
186                     } else {
187                         REPORTER_ASSERT(reporter, origTexInfo.fID == genTexInfo.fID);
188                     }
189                 } else {
190                     ERRORF(reporter, "Failed to get GrGLTextureInfo");
191                 }
192 #ifdef SK_VULKAN
193             } else if (GrBackendApi::kVulkan == genBackendTex.backend()) {
194                 GrVkImageInfo genImageInfo;
195                 GrVkImageInfo origImageInfo;
196                 if (genBackendTex.getVkImageInfo(&genImageInfo) &&
197                     backendTex.getVkImageInfo(&origImageInfo)) {
198                     if (willUseMips && GrMipMapped::kNo == mipMapped) {
199                         // We did a copy so the texture IDs should be different
200                         REPORTER_ASSERT(reporter, origImageInfo.fImage != genImageInfo.fImage);
201                     } else {
202                         REPORTER_ASSERT(reporter, origImageInfo.fImage == genImageInfo.fImage);
203                     }
204                 } else {
205                     ERRORF(reporter, "Failed to get GrVkImageInfo");
206                 }
207 #endif
208 #ifdef SK_METAL
209             } else if (GrBackendApi::kMetal == genBackendTex.backend()) {
210                 GrMtlTextureInfo genImageInfo;
211                 GrMtlTextureInfo origImageInfo;
212                 if (genBackendTex.getMtlTextureInfo(&genImageInfo) &&
213                     backendTex.getMtlTextureInfo(&origImageInfo)) {
214                     if (willUseMips && GrMipMapped::kNo == mipMapped) {
215                         // We did a copy so the texture IDs should be different
216                         REPORTER_ASSERT(reporter, origImageInfo.fTexture != genImageInfo.fTexture);
217                     } else {
218                         REPORTER_ASSERT(reporter, origImageInfo.fTexture == genImageInfo.fTexture);
219                     }
220                 } else {
221                     ERRORF(reporter, "Failed to get GrMtlTextureInfo");
222                 }
223 #endif
224             } else {
225                 REPORTER_ASSERT(reporter, false);
226             }
227 
228             // Must make sure the uses of the backend texture have finished (we possibly have a
229             // queued up copy) before we delete the backend texture.
230             context->flush();
231 
232             context->priv().getGpu()->testingOnly_flushGpuAndSync();
233 
234             context->deleteBackendTexture(backendTex);
235         }
236     }
237 }
238 
239 // Test that when we call makeImageSnapshot on an SkSurface we retains the same mip status as the
240 // resource we took the snapshot of.
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrImageSnapshotMipMappedTest,reporter,ctxInfo)241 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrImageSnapshotMipMappedTest, reporter, ctxInfo) {
242     GrContext* context = ctxInfo.grContext();
243     if (!context->priv().caps()->mipMapSupport()) {
244         return;
245     }
246 
247     auto resourceProvider = context->priv().resourceProvider();
248 
249     for (auto willUseMips : {false, true}) {
250         for (auto isWrapped : {false, true}) {
251             GrMipMapped mipMapped = willUseMips ? GrMipMapped::kYes : GrMipMapped::kNo;
252             sk_sp<SkSurface> surface;
253             GrBackendTexture backendTex = context->createBackendTexture(
254                     kSize, kSize, kRGBA_8888_SkColorType,
255                     SkColors::kTransparent, mipMapped, GrRenderable::kYes, GrProtected::kNo);
256             if (isWrapped) {
257                 surface = SkSurface::MakeFromBackendTexture(context,
258                                                             backendTex,
259                                                             kTopLeft_GrSurfaceOrigin,
260                                                             0,
261                                                             kRGBA_8888_SkColorType,
262                                                             nullptr,
263                                                             nullptr);
264             } else {
265                 SkImageInfo info = SkImageInfo::Make(kSize, kSize, kRGBA_8888_SkColorType,
266                                                      kPremul_SkAlphaType);
267                 surface = SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, info, 0,
268                                                       kTopLeft_GrSurfaceOrigin, nullptr,
269                                                       willUseMips);
270             }
271             REPORTER_ASSERT(reporter, surface);
272             if (!surface) {
273                 context->deleteBackendTexture(backendTex);
274             }
275             SkGpuDevice* device = ((SkSurface_Gpu*)surface.get())->getDevice();
276             GrTextureProxy* texProxy = device->accessRenderTargetContext()->asTextureProxy();
277             REPORTER_ASSERT(reporter, mipMapped == texProxy->mipMapped());
278 
279             texProxy->instantiate(resourceProvider);
280             GrTexture* texture = texProxy->peekTexture();
281             REPORTER_ASSERT(reporter, mipMapped == texture->texturePriv().mipMapped());
282 
283             sk_sp<SkImage> image = surface->makeImageSnapshot();
284             REPORTER_ASSERT(reporter, image);
285             if (!image) {
286                 context->deleteBackendTexture(backendTex);
287             }
288             texProxy = as_IB(image)->peekProxy();
289             REPORTER_ASSERT(reporter, mipMapped == texProxy->mipMapped());
290 
291             texProxy->instantiate(resourceProvider);
292             texture = texProxy->peekTexture();
293             REPORTER_ASSERT(reporter, mipMapped == texture->texturePriv().mipMapped());
294 
295             // Must flush the context to make sure all the cmds (copies, etc.) from above are sent
296             // to the gpu before we delete the backendHandle.
297             context->flush();
298             context->priv().getGpu()->testingOnly_flushGpuAndSync();
299             context->deleteBackendTexture(backendTex);
300         }
301     }
302 }
303 
304 // Test that we don't create a mip mapped texture if the size is 1x1 even if the filter mode is set
305 // to use mips. This test passes by not crashing or hitting asserts in code.
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(Gr1x1TextureMipMappedTest,reporter,ctxInfo)306 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(Gr1x1TextureMipMappedTest, reporter, ctxInfo) {
307     GrContext* context = ctxInfo.grContext();
308     if (!context->priv().caps()->mipMapSupport()) {
309         return;
310     }
311 
312     // Make surface to draw into
313     SkImageInfo info = SkImageInfo::MakeN32(16, 16, kPremul_SkAlphaType);
314     sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info);
315 
316     // Make 1x1 raster bitmap
317     SkBitmap bmp;
318     bmp.allocN32Pixels(1, 1);
319     SkPMColor* pixel = reinterpret_cast<SkPMColor*>(bmp.getPixels());
320     *pixel = 0;
321 
322     sk_sp<SkImage> bmpImage = SkImage::MakeFromBitmap(bmp);
323 
324     // Make sure we scale so we don't optimize out the use of mips.
325     surface->getCanvas()->scale(0.5f, 0.5f);
326 
327     SkPaint paint;
328     // This should upload the image to a non mipped GrTextureProxy.
329     surface->getCanvas()->drawImage(bmpImage, 0, 0, &paint);
330     surface->flush();
331 
332     // Now set the filter quality to high so we use mip maps. We should find the non mipped texture
333     // in the cache for the SkImage. Since the texture is 1x1 we should just use that texture
334     // instead of trying to do a copy to a mipped texture.
335     paint.setFilterQuality(kHigh_SkFilterQuality);
336     surface->getCanvas()->drawImage(bmpImage, 0, 0, &paint);
337     surface->flush();
338 }
339 
340 // Create a new render target and draw 'mipmapProxy' into it using the provided 'filter'.
draw_mipmap_into_new_render_target(GrDrawingManager * drawingManager,GrProxyProvider * proxyProvider,GrColorType colorType,sk_sp<GrTextureProxy> mipmapProxy,GrSamplerState::Filter filter)341 static sk_sp<GrRenderTargetContext> draw_mipmap_into_new_render_target(
342         GrDrawingManager* drawingManager, GrProxyProvider* proxyProvider, GrColorType colorType,
343         sk_sp<GrTextureProxy> mipmapProxy, GrSamplerState::Filter filter) {
344     GrSurfaceDesc desc;
345     desc.fWidth = 1;
346     desc.fHeight = 1;
347     desc.fConfig = mipmapProxy->config();
348     sk_sp<GrSurfaceProxy> renderTarget = proxyProvider->createProxy(
349             mipmapProxy->backendFormat(), desc, GrRenderable::kYes, 1, kTopLeft_GrSurfaceOrigin,
350             SkBackingFit::kApprox, SkBudgeted::kYes, GrProtected::kNo);
351     sk_sp<GrRenderTargetContext> rtc = drawingManager->makeRenderTargetContext(
352             std::move(renderTarget), colorType, nullptr, nullptr, true);
353     rtc->drawTexture(GrNoClip(), mipmapProxy, filter, SkBlendMode::kSrcOver, {1,1,1,1},
354                      SkRect::MakeWH(4, 4), SkRect::MakeWH(1,1), GrAA::kYes, GrQuadAAFlags::kAll,
355                      SkCanvas::kFast_SrcRectConstraint, SkMatrix::I(), nullptr);
356     return rtc;
357 }
358 
359 // Test that two opLists using the same mipmaps both depend on the same GrTextureResolveRenderTask.
DEF_GPUTEST_FOR_ALL_CONTEXTS(GrManyDependentsMipMappedTest,reporter,ctxInfo)360 DEF_GPUTEST_FOR_ALL_CONTEXTS(GrManyDependentsMipMappedTest, reporter, ctxInfo) {
361     GrContext* context = ctxInfo.grContext();
362     if (!context->priv().caps()->mipMapSupport()) {
363         return;
364     }
365 
366     GrBackendFormat format = context->defaultBackendFormat(
367             kRGBA_8888_SkColorType, GrRenderable::kYes);
368     GrPixelConfig config = kRGBA_8888_GrPixelConfig;
369     GrColorType colorType = GrColorType::kRGBA_8888;
370 
371     GrDrawingManager* drawingManager = context->priv().drawingManager();
372     GrProxyProvider* proxyProvider = context->priv().proxyProvider();
373 
374     // Create a mipmapped render target.
375     GrSurfaceDesc desc;
376     desc.fWidth = 4;
377     desc.fHeight = 4;
378     desc.fConfig = config;
379     sk_sp<GrTextureProxy> mipmapProxy = proxyProvider->createMipMapProxy(
380             format, desc, GrRenderable::kYes, 1, kTopLeft_GrSurfaceOrigin, SkBudgeted::kYes,
381             GrProtected::kNo);
382 
383     // Render something to dirty the mips.
384     sk_sp<GrRenderTargetContext> mipmapRTC = drawingManager->makeRenderTargetContext(
385             mipmapProxy, colorType, nullptr, nullptr, true);
386     mipmapRTC->clear(nullptr, {.1f,.2f,.3f,.4f}, GrRenderTargetContext::CanClearFullscreen::kYes);
387     REPORTER_ASSERT(reporter, mipmapProxy->mipMapsAreDirty());
388     REPORTER_ASSERT(reporter, mipmapProxy->getLastRenderTask());
389     // mipmapProxy's last render task should just be the opList containing the clear at this point.
390     REPORTER_ASSERT(
391             reporter, mipmapRTC->testingOnly_PeekLastOpList() == mipmapProxy->getLastRenderTask());
392 
393     // Draw the dirty mipmap texture into a render target.
394     sk_sp<GrRenderTargetContext> rtc1 = draw_mipmap_into_new_render_target(
395             drawingManager, proxyProvider, colorType, mipmapProxy, GrSamplerState::Filter::kMipMap);
396 
397     // Make sure the texture's mipmaps are now clean, and its last render task has switched from the
398     // opList that drew to it, to the task that resolved its mips.
399     GrRenderTask* initialMipmapRegenTask = mipmapProxy->getLastRenderTask();
400     REPORTER_ASSERT(reporter, initialMipmapRegenTask);
401     REPORTER_ASSERT(reporter, initialMipmapRegenTask != mipmapRTC->testingOnly_PeekLastOpList());
402     REPORTER_ASSERT(
403             reporter, rtc1->testingOnly_PeekLastOpList()->dependsOn(initialMipmapRegenTask));
404     REPORTER_ASSERT(reporter, !mipmapProxy->mipMapsAreDirty());
405     SkASSERT(!mipmapProxy->mipMapsAreDirty());
406 
407     // Draw the now-clean mipmap texture into a second target.
408     sk_sp<GrRenderTargetContext> rtc2 = draw_mipmap_into_new_render_target(
409             drawingManager, proxyProvider, colorType, mipmapProxy, GrSamplerState::Filter::kMipMap);
410 
411     // Make sure the mipmap texture still has the same regen task.
412     REPORTER_ASSERT(reporter, mipmapProxy->getLastRenderTask() == initialMipmapRegenTask);
413     REPORTER_ASSERT(
414             reporter, rtc2->testingOnly_PeekLastOpList()->dependsOn(initialMipmapRegenTask));
415     SkASSERT(!mipmapProxy->mipMapsAreDirty());
416 
417     // Reset everything so we can go again, this time with the first draw not mipmapped.
418     context->flush();
419 
420     // Render something to dirty the mips.
421     mipmapRTC->clear(nullptr, {.1f,.2f,.3f,.4f}, GrRenderTargetContext::CanClearFullscreen::kYes);
422     REPORTER_ASSERT(reporter, mipmapProxy->mipMapsAreDirty());
423     REPORTER_ASSERT(reporter, mipmapProxy->getLastRenderTask());
424     // mipmapProxy's last render task should just be the opList containing the clear at this point.
425     REPORTER_ASSERT(
426             reporter, mipmapRTC->testingOnly_PeekLastOpList() == mipmapProxy->getLastRenderTask());
427 
428     // Draw the dirty mipmap texture into a render target, but don't do mipmap filtering.
429     rtc1 = draw_mipmap_into_new_render_target(
430             drawingManager, proxyProvider, colorType, mipmapProxy, GrSamplerState::Filter::kBilerp);
431 
432     // Make sure the texture's mipmaps are still dirty, and its last render task has not changed.
433     REPORTER_ASSERT(reporter, mipmapProxy->mipMapsAreDirty());
434     REPORTER_ASSERT(
435             reporter, mipmapRTC->testingOnly_PeekLastOpList() == mipmapProxy->getLastRenderTask());
436 
437     // Draw the stil-dirty mipmap texture into a second target.
438     rtc2 = draw_mipmap_into_new_render_target(
439             drawingManager, proxyProvider, colorType, mipmapProxy, GrSamplerState::Filter::kMipMap);
440 
441     // Make sure the mipmap texture now has a new last render task.
442     REPORTER_ASSERT(reporter, mipmapProxy->getLastRenderTask());
443     REPORTER_ASSERT(reporter,
444             mipmapRTC->testingOnly_PeekLastOpList() != mipmapProxy->getLastRenderTask());
445     REPORTER_ASSERT(reporter,
446             rtc2->testingOnly_PeekLastOpList()->dependsOn(mipmapProxy->getLastRenderTask()));
447     SkASSERT(!mipmapProxy->mipMapsAreDirty());
448 }
449