1 /*
2 * Copyright 2018 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. It relies on static intializers to work
9
10 #include "include/core/SkTypes.h"
11
12 #if defined(SK_VULKAN)
13
14 #include "include/gpu/vk/GrVkVulkan.h"
15
16 #include "tests/Test.h"
17
18 #include "include/core/SkImage.h"
19 #include "include/gpu/GrBackendSurface.h"
20 #include "include/gpu/GrTexture.h"
21 #include "include/gpu/vk/GrVkTypes.h"
22 #include "src/gpu/GrContextPriv.h"
23 #include "src/gpu/GrRenderTargetContext.h"
24 #include "src/gpu/GrTextureProxy.h"
25 #include "src/gpu/SkGpuDevice.h"
26 #include "src/gpu/vk/GrVkGpu.h"
27 #include "src/gpu/vk/GrVkImageLayout.h"
28 #include "src/gpu/vk/GrVkTexture.h"
29 #include "src/image/SkImage_Base.h"
30 #include "src/image/SkSurface_Gpu.h"
31
DEF_GPUTEST_FOR_VULKAN_CONTEXT(VkImageLayoutTest,reporter,ctxInfo)32 DEF_GPUTEST_FOR_VULKAN_CONTEXT(VkImageLayoutTest, reporter, ctxInfo) {
33 GrContext* context = ctxInfo.grContext();
34
35 GrBackendTexture backendTex = context->createBackendTexture(1, 1,
36 kRGBA_8888_SkColorType,
37 SkColors::kTransparent,
38 GrMipMapped::kNo,
39 GrRenderable::kNo,
40 GrProtected::kNo);
41 REPORTER_ASSERT(reporter, backendTex.isValid());
42
43 GrVkImageInfo info;
44 REPORTER_ASSERT(reporter, backendTex.getVkImageInfo(&info));
45 VkImageLayout initLayout = info.fImageLayout;
46
47 // Verify that setting that layout via a copy of a backendTexture is reflected in all the
48 // backendTextures.
49 GrBackendTexture backendTexCopy = backendTex;
50 REPORTER_ASSERT(reporter, backendTexCopy.getVkImageInfo(&info));
51 REPORTER_ASSERT(reporter, initLayout == info.fImageLayout);
52
53 backendTexCopy.setVkImageLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
54
55 REPORTER_ASSERT(reporter, backendTex.getVkImageInfo(&info));
56 REPORTER_ASSERT(reporter, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL == info.fImageLayout);
57
58 REPORTER_ASSERT(reporter, backendTexCopy.getVkImageInfo(&info));
59 REPORTER_ASSERT(reporter, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL == info.fImageLayout);
60
61 // Setting back the layout since we didn't actually change it
62 backendTex.setVkImageLayout(initLayout);
63
64 sk_sp<SkImage> wrappedImage = SkImage::MakeFromTexture(context, backendTex,
65 kTopLeft_GrSurfaceOrigin,
66 kRGBA_8888_SkColorType,
67 kPremul_SkAlphaType, nullptr);
68 REPORTER_ASSERT(reporter, wrappedImage.get());
69
70 const GrSurfaceProxyView* view = as_IB(wrappedImage)->view(context);
71 REPORTER_ASSERT(reporter, view);
72 REPORTER_ASSERT(reporter, view->proxy()->isInstantiated());
73 GrTexture* texture = view->proxy()->peekTexture();
74 REPORTER_ASSERT(reporter, texture);
75
76 // Verify that modifying the layout via the GrVkTexture is reflected in the GrBackendTexture
77 GrVkTexture* vkTexture = static_cast<GrVkTexture*>(texture);
78 REPORTER_ASSERT(reporter, initLayout == vkTexture->currentLayout());
79 vkTexture->updateImageLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
80
81 REPORTER_ASSERT(reporter, backendTex.getVkImageInfo(&info));
82 REPORTER_ASSERT(reporter, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL == info.fImageLayout);
83
84 GrBackendTexture backendTexImage = wrappedImage->getBackendTexture(false);
85 REPORTER_ASSERT(reporter, backendTexImage.getVkImageInfo(&info));
86 REPORTER_ASSERT(reporter, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL == info.fImageLayout);
87
88 // Verify that modifying the layout via the GrBackendTexutre is reflected in the GrVkTexture
89 backendTexImage.setVkImageLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
90 REPORTER_ASSERT(reporter, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL == vkTexture->currentLayout());
91
92 vkTexture->updateImageLayout(initLayout);
93
94 REPORTER_ASSERT(reporter, backendTex.getVkImageInfo(&info));
95 REPORTER_ASSERT(reporter, initLayout == info.fImageLayout);
96
97 REPORTER_ASSERT(reporter, backendTexCopy.getVkImageInfo(&info));
98 REPORTER_ASSERT(reporter, initLayout == info.fImageLayout);
99
100 REPORTER_ASSERT(reporter, backendTexImage.getVkImageInfo(&info));
101 REPORTER_ASSERT(reporter, initLayout == info.fImageLayout);
102
103 // Check that we can do things like assigning the backend texture to invalid one, assign an
104 // invalid one, assin a backend texture to inself etc. Success here is that we don't hit any of
105 // our ref counting asserts.
106 REPORTER_ASSERT(reporter, GrBackendTexture::TestingOnly_Equals(backendTex, backendTexCopy));
107
108 GrBackendTexture invalidTexture;
109 REPORTER_ASSERT(reporter, !invalidTexture.isValid());
110 REPORTER_ASSERT(reporter, !GrBackendTexture::TestingOnly_Equals(invalidTexture, backendTexCopy));
111
112 backendTexCopy = invalidTexture;
113 REPORTER_ASSERT(reporter, !backendTexCopy.isValid());
114 REPORTER_ASSERT(reporter, !GrBackendTexture::TestingOnly_Equals(invalidTexture, backendTexCopy));
115
116 invalidTexture = backendTex;
117 REPORTER_ASSERT(reporter, invalidTexture.isValid());
118 REPORTER_ASSERT(reporter, GrBackendTexture::TestingOnly_Equals(invalidTexture, backendTex));
119
120 invalidTexture = static_cast<decltype(invalidTexture)&>(invalidTexture);
121 REPORTER_ASSERT(reporter, invalidTexture.isValid());
122 REPORTER_ASSERT(reporter, GrBackendTexture::TestingOnly_Equals(invalidTexture, invalidTexture));
123
124 context->deleteBackendTexture(backendTex);
125 }
126
testing_release_proc(void * ctx)127 static void testing_release_proc(void* ctx) {
128 int* count = (int*)ctx;
129 *count += 1;
130 }
131
132 // Test to make sure we don't call our release proc on an image until we've transferred it back to
133 // its original queue family.
DEF_GPUTEST_FOR_VULKAN_CONTEXT(VkReleaseExternalQueueTest,reporter,ctxInfo)134 DEF_GPUTEST_FOR_VULKAN_CONTEXT(VkReleaseExternalQueueTest, reporter, ctxInfo) {
135 GrContext* context = ctxInfo.grContext();
136 GrGpu* gpu = context->priv().getGpu();
137 GrVkGpu* vkGpu = static_cast<GrVkGpu*>(gpu);
138 if (!vkGpu->vkCaps().supportsExternalMemory()) {
139 return;
140 }
141
142 for (bool useExternal : {false, true}) {
143 GrBackendTexture backendTex = context->createBackendTexture(1, 1,
144 kRGBA_8888_SkColorType,
145 SkColors::kTransparent,
146 GrMipMapped::kNo,
147 GrRenderable::kNo,
148 GrProtected::kNo);
149 sk_sp<SkImage> image;
150 int count = 0;
151 if (useExternal) {
152 // Make a backend texture with an external queue family;
153 GrVkImageInfo vkInfo;
154 if (!backendTex.getVkImageInfo(&vkInfo)) {
155 return;
156 }
157 vkInfo.fCurrentQueueFamily = VK_QUEUE_FAMILY_EXTERNAL;
158
159 GrBackendTexture vkExtTex(1, 1, vkInfo);
160 REPORTER_ASSERT(reporter, vkExtTex.isValid());
161 image = SkImage::MakeFromTexture(context, vkExtTex,
162 kTopLeft_GrSurfaceOrigin,
163 kRGBA_8888_SkColorType,
164 kPremul_SkAlphaType,
165 nullptr, testing_release_proc,
166 (void*)&count);
167
168 } else {
169 image = SkImage::MakeFromTexture(context, backendTex,
170 kTopLeft_GrSurfaceOrigin,
171 kRGBA_8888_SkColorType,
172 kPremul_SkAlphaType,
173 nullptr, testing_release_proc,
174 (void*)&count);
175 }
176
177 if (!image) {
178 continue;
179 }
180
181 REPORTER_ASSERT(reporter, !count);
182
183 GrTexture* texture = image->getTexture();
184 REPORTER_ASSERT(reporter, texture);
185 GrVkTexture* vkTex = static_cast<GrVkTexture*>(texture);
186
187 if (useExternal) {
188 // Testing helper so we claim that we don't need to transition from our fake external
189 // queue first.
190 vkTex->setCurrentQueueFamilyToGraphicsQueue(vkGpu);
191 }
192
193 image.reset();
194
195 // Resetting the image should only trigger the release proc if we are not using an external
196 // queue. When using an external queue when we free the SkImage and the underlying
197 // GrTexture, we submit a queue transition on the command buffer.
198 if (useExternal) {
199 REPORTER_ASSERT(reporter, !count);
200 } else {
201 REPORTER_ASSERT(reporter, count == 1);
202 }
203
204 gpu->testingOnly_flushGpuAndSync();
205
206 // Now that we flushed and waited the release proc should have be triggered.
207 REPORTER_ASSERT(reporter, count == 1);
208
209 context->deleteBackendTexture(backendTex);
210 }
211 }
212
213 // Test to make sure we transition to the original queue when requests for prepareforexternalio are
214 // in flush calls
DEF_GPUTEST_FOR_VULKAN_CONTEXT(VkPrepareForExternalIOQueueTransitionTest,reporter,ctxInfo)215 DEF_GPUTEST_FOR_VULKAN_CONTEXT(VkPrepareForExternalIOQueueTransitionTest, reporter, ctxInfo) {
216 GrContext* context = ctxInfo.grContext();
217
218 GrVkGpu* vkGpu = static_cast<GrVkGpu*>(context->priv().getGpu());
219 if (!vkGpu->vkCaps().supportsExternalMemory()) {
220 return;
221 }
222
223 for (bool useSurface : {false, true}) {
224 for (bool preparePresent : {false, true}) {
225 if (!useSurface && preparePresent) {
226 // We don't set textures to present
227 continue;
228 }
229 GrBackendTexture backendTex = context->createBackendTexture(
230 4, 4, kRGBA_8888_SkColorType,
231 SkColors::kTransparent, GrMipMapped::kNo,
232 useSurface ? GrRenderable::kYes : GrRenderable::kNo,
233 GrProtected::kNo);
234
235 // Make a backend texture with an external queue family and general layout.
236 GrVkImageInfo vkInfo;
237 if (!backendTex.getVkImageInfo(&vkInfo)) {
238 return;
239 }
240
241 // We can't actually make an external texture in our test. However, we lie and say it is
242 // and then will manually go and swap the queue to the graphics queue once we wrap it.
243 if (preparePresent) {
244 // We don't transition to present to things that are going to external for foreign
245 // queues.
246 vkInfo.fCurrentQueueFamily = vkGpu->queueIndex();
247 } else {
248 vkInfo.fCurrentQueueFamily = VK_QUEUE_FAMILY_EXTERNAL;
249 }
250
251 GrBackendTexture vkExtTex(1, 1, vkInfo);
252
253 sk_sp<SkImage> image;
254 sk_sp<SkSurface> surface;
255 GrTexture* texture;
256 if (useSurface) {
257 surface = SkSurface::MakeFromBackendTexture(context, vkExtTex,
258 kTopLeft_GrSurfaceOrigin, 0, kRGBA_8888_SkColorType, nullptr, nullptr);
259 REPORTER_ASSERT(reporter, surface.get());
260 if (!surface) {
261 continue;
262 }
263 SkSurface_Gpu* gpuSurface = static_cast<SkSurface_Gpu*>(surface.get());
264 auto* rtc = gpuSurface->getDevice()->accessRenderTargetContext();
265 texture = rtc->asTextureProxy()->peekTexture();
266 } else {
267 image = SkImage::MakeFromTexture(context, vkExtTex, kTopLeft_GrSurfaceOrigin,
268 kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr, nullptr, nullptr);
269
270 REPORTER_ASSERT(reporter, image.get());
271 if (!image) {
272 continue;
273 }
274
275 texture = image->getTexture();
276 }
277
278 REPORTER_ASSERT(reporter, texture);
279 GrVkTexture* vkTex = static_cast<GrVkTexture*>(texture);
280
281 // Testing helper so we claim that we don't need to transition from our fake external
282 // queue first.
283 vkTex->setCurrentQueueFamilyToGraphicsQueue(vkGpu);
284
285 GrBackendTexture newBackendTexture;
286 if (useSurface) {
287 newBackendTexture = surface->getBackendTexture(
288 SkSurface::kFlushRead_TextureHandleAccess);
289 } else {
290 newBackendTexture = image->getBackendTexture(false);
291 }
292 GrVkImageInfo newVkInfo;
293 REPORTER_ASSERT(reporter, newBackendTexture.getVkImageInfo(&newVkInfo));
294 REPORTER_ASSERT(reporter, newVkInfo.fCurrentQueueFamily == vkGpu->queueIndex());
295 VkImageLayout oldLayout = newVkInfo.fImageLayout;
296
297 GrPrepareForExternalIORequests externalRequests;
298 SkImage* imagePtr;
299 SkSurface* surfacePtr;
300 if (useSurface) {
301 externalRequests.fNumSurfaces = 1;
302 surfacePtr = surface.get();
303 externalRequests.fSurfaces = &surfacePtr;
304 externalRequests.fPrepareSurfaceForPresent = &preparePresent;
305 } else {
306 externalRequests.fNumImages = 1;
307 imagePtr = image.get();
308 externalRequests.fImages = &imagePtr;
309
310 }
311 context->flush(GrFlushInfo(), externalRequests);
312
313 if (useSurface) {
314 newBackendTexture = surface->getBackendTexture(
315 SkSurface::kFlushRead_TextureHandleAccess);
316 } else {
317 newBackendTexture = image->getBackendTexture(false);
318 }
319 REPORTER_ASSERT(reporter, newBackendTexture.getVkImageInfo(&newVkInfo));
320 if (preparePresent) {
321 REPORTER_ASSERT(reporter, newVkInfo.fCurrentQueueFamily == vkGpu->queueIndex());
322 REPORTER_ASSERT(reporter,
323 newVkInfo.fImageLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
324 } else {
325 REPORTER_ASSERT(reporter, newVkInfo.fCurrentQueueFamily == VK_QUEUE_FAMILY_EXTERNAL);
326 REPORTER_ASSERT(reporter, newVkInfo.fImageLayout == oldLayout);
327 }
328
329 GrFlushInfo flushInfo;
330 flushInfo.fFlags = kSyncCpu_GrFlushFlag;
331 context->flush(flushInfo);
332 context->deleteBackendTexture(backendTex);
333 }
334 }
335 }
336
337 // This test is disabled because it executes illegal vulkan calls which cause the validations layers
338 // to fail and makes us assert. Once fixed to use a valid vulkan call sequence it should be
339 // renenabled, see skbug.com/8936.
340 #if 0
341 // Test to make sure we transition from the EXTERNAL queue even when no layout transition is needed.
342 DEF_GPUTEST_FOR_VULKAN_CONTEXT(VkTransitionExternalQueueTest, reporter, ctxInfo) {
343 GrContext* context = ctxInfo.grContext();
344 GrGpu* gpu = context->priv().getGpu();
345 GrVkGpu* vkGpu = static_cast<GrVkGpu*>(gpu);
346 if (!vkGpu->vkCaps().supportsExternalMemory()) {
347 return;
348 }
349
350 GrBackendTexture backendTex = context->createBackendTexture(
351 1, 1, kRGBA_8888_SkColorType,
352 SkColors::kTransparent, GrMipMapped::kNo, GrRenderable::kNo);
353 sk_sp<SkImage> image;
354 // Make a backend texture with an external queue family and general layout.
355 GrVkImageInfo vkInfo;
356 if (!backendTex.getVkImageInfo(&vkInfo)) {
357 return;
358 }
359 vkInfo.fCurrentQueueFamily = VK_QUEUE_FAMILY_EXTERNAL;
360 // Use a read-only layout as these are the ones where we can otherwise skip a transition.
361 vkInfo.fImageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
362
363 GrBackendTexture vkExtTex(1, 1, vkInfo);
364 REPORTER_ASSERT(reporter, vkExtTex.isValid());
365 image = SkImage::MakeFromTexture(context, vkExtTex, kTopLeft_GrSurfaceOrigin,
366 kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr, nullptr,
367 nullptr);
368
369 if (!image) {
370 return;
371 }
372
373 GrTexture* texture = image->getTexture();
374 REPORTER_ASSERT(reporter, texture);
375 GrVkTexture* vkTex = static_cast<GrVkTexture*>(texture);
376
377 // Change our backend texture to the internal queue, with the same layout. This should force a
378 // queue transition even though the layouts match.
379 vkTex->setImageLayout(vkGpu, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, 0,
380 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, false, false);
381
382 // Get our image info again and make sure we transitioned queues.
383 GrBackendTexture newBackendTexture = image->getBackendTexture(true);
384 GrVkImageInfo newVkInfo;
385 REPORTER_ASSERT(reporter, newBackendTexture.getVkImageInfo(&newVkInfo));
386 REPORTER_ASSERT(reporter, newVkInfo.fCurrentQueueFamily == vkGpu->queueIndex());
387
388 image.reset();
389 gpu->testingOnly_flushGpuAndSync();
390 context->deleteBackendTexture(backendTex);
391 }
392 #endif
393
394 #endif
395