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