1 // Copyright (C) 2023 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <string>
16 
17 #include "GfxstreamEnd2EndTestUtils.h"
18 #include "GfxstreamEnd2EndTests.h"
19 #include "gfxstream/RutabagaLayerTestUtils.h"
20 
21 namespace gfxstream {
22 namespace tests {
23 namespace {
24 
25 using testing::Eq;
26 using testing::Ge;
27 using testing::IsEmpty;
28 using testing::IsNull;
29 using testing::Not;
30 using testing::NotNull;
31 
32 class GfxstreamEnd2EndVkSnapshotImageTest : public GfxstreamEnd2EndTest {};
33 
TEST_P(GfxstreamEnd2EndVkSnapshotImageTest,PreserveImageHandle)34 TEST_P(GfxstreamEnd2EndVkSnapshotImageTest, PreserveImageHandle) {
35     auto [instance, physicalDevice, device, queue, queueFamilyIndex] =
36         VK_ASSERT(SetUpTypicalVkTestEnvironment());
37 
38     const uint32_t width = 32;
39     const uint32_t height = 32;
40 
41     const vkhpp::ImageCreateInfo imageCreateInfo = {
42         .pNext = nullptr,
43         .imageType = vkhpp::ImageType::e2D,
44         .extent.width = width,
45         .extent.height = height,
46         .extent.depth = 1,
47         .mipLevels = 1,
48         .arrayLayers = 1,
49         .format = vkhpp::Format::eR8G8B8A8Unorm,
50         .tiling = vkhpp::ImageTiling::eOptimal,
51         .initialLayout = vkhpp::ImageLayout::eUndefined,
52         .usage = vkhpp::ImageUsageFlagBits::eSampled | vkhpp::ImageUsageFlagBits::eTransferDst |
53                  vkhpp::ImageUsageFlagBits::eTransferSrc,
54         .sharingMode = vkhpp::SharingMode::eExclusive,
55         .samples = vkhpp::SampleCountFlagBits::e1,
56     };
57     auto image = device->createImageUnique(imageCreateInfo).value;
58 
59     vkhpp::MemoryRequirements imageMemoryRequirements{};
60     device->getImageMemoryRequirements(*image, &imageMemoryRequirements);
61 
62     const uint32_t imageMemoryIndex = utils::getMemoryType(
63         physicalDevice, imageMemoryRequirements, vkhpp::MemoryPropertyFlagBits::eDeviceLocal);
64     ASSERT_THAT(imageMemoryIndex, Not(Eq(-1)));
65 
66     const vkhpp::MemoryAllocateInfo imageMemoryAllocateInfo = {
67         .allocationSize = imageMemoryRequirements.size,
68         .memoryTypeIndex = imageMemoryIndex,
69     };
70 
71     auto imageMemory = device->allocateMemoryUnique(imageMemoryAllocateInfo).value;
72     ASSERT_THAT(imageMemory, IsValidHandle());
73 
74     SnapshotSaveAndLoad();
75 
76     ASSERT_THAT(device->bindImageMemory(*image, *imageMemory, 0), IsVkSuccess());
77 }
78 
79 // b/346415931
80 // We used to have an issue that the handles mismatch when running more device
81 // create calls. The first device always work but the second might break.
TEST_P(GfxstreamEnd2EndVkSnapshotImageTest,MultipleDevicesPreserveHandles)82 TEST_P(GfxstreamEnd2EndVkSnapshotImageTest, MultipleDevicesPreserveHandles) {
83     auto [instance, physicalDevice, device, queue, queueFamilyIndex] =
84         VK_ASSERT(SetUpTypicalVkTestEnvironment());
85 
86     uint32_t graphicsQueueFamilyIndex = -1;
87     {
88         const auto props = physicalDevice.getQueueFamilyProperties();
89         for (uint32_t i = 0; i < props.size(); i++) {
90             const auto& prop = props[i];
91             if (prop.queueFlags & vkhpp::QueueFlagBits::eGraphics) {
92                 graphicsQueueFamilyIndex = i;
93                 break;
94             }
95         }
96     }
97     ASSERT_THAT(graphicsQueueFamilyIndex, Not(Eq(-1)));
98     const float queuePriority = 1.0f;
99     const vkhpp::DeviceQueueCreateInfo deviceQueueCreateInfo = {
100         .queueFamilyIndex = graphicsQueueFamilyIndex,
101         .queueCount = 1,
102         .pQueuePriorities = &queuePriority,
103     };
104     std::vector<const char*> deviceExtensions = {
105         VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME,
106         VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME,
107     };
108     const vkhpp::DeviceCreateInfo deviceCreateInfo = {
109         .pNext = nullptr,
110         .pQueueCreateInfos = &deviceQueueCreateInfo,
111         .queueCreateInfoCount = 1,
112         .enabledLayerCount = 0,
113         .ppEnabledLayerNames = nullptr,
114         .enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size()),
115         .ppEnabledExtensionNames = deviceExtensions.data(),
116     };
117     auto device2 = physicalDevice.createDeviceUnique(deviceCreateInfo).value;
118     ASSERT_THAT(device2, IsValidHandle());
119 
120     const uint32_t width = 32;
121     const uint32_t height = 32;
122 
123     const vkhpp::ImageCreateInfo imageCreateInfo = {
124         .pNext = nullptr,
125         .imageType = vkhpp::ImageType::e2D,
126         .extent.width = width,
127         .extent.height = height,
128         .extent.depth = 1,
129         .mipLevels = 1,
130         .arrayLayers = 1,
131         .format = vkhpp::Format::eR8G8B8A8Unorm,
132         .tiling = vkhpp::ImageTiling::eOptimal,
133         .initialLayout = vkhpp::ImageLayout::eUndefined,
134         .usage = vkhpp::ImageUsageFlagBits::eSampled | vkhpp::ImageUsageFlagBits::eTransferDst |
135                  vkhpp::ImageUsageFlagBits::eTransferSrc,
136         .sharingMode = vkhpp::SharingMode::eExclusive,
137         .samples = vkhpp::SampleCountFlagBits::e1,
138     };
139     auto image = device->createImageUnique(imageCreateInfo).value;
140 
141     vkhpp::MemoryRequirements imageMemoryRequirements{};
142     device->getImageMemoryRequirements(*image, &imageMemoryRequirements);
143 
144     const uint32_t imageMemoryIndex = utils::getMemoryType(
145         physicalDevice, imageMemoryRequirements, vkhpp::MemoryPropertyFlagBits::eDeviceLocal);
146     ASSERT_THAT(imageMemoryIndex, Not(Eq(-1)));
147 
148     const vkhpp::MemoryAllocateInfo imageMemoryAllocateInfo = {
149         .allocationSize = imageMemoryRequirements.size,
150         .memoryTypeIndex = imageMemoryIndex,
151     };
152 
153     auto imageMemory = device->allocateMemoryUnique(imageMemoryAllocateInfo).value;
154     ASSERT_THAT(imageMemory, IsValidHandle());
155 
156     ASSERT_THAT(device->bindImageMemory(*image, *imageMemory, 0), IsVkSuccess());
157 
158     // No device lost on snapshot load.
159     SnapshotSaveAndLoad();
160 }
161 
TEST_P(GfxstreamEnd2EndVkSnapshotImageTest,ImageViewDependency)162 TEST_P(GfxstreamEnd2EndVkSnapshotImageTest, ImageViewDependency) {
163     auto [instance, physicalDevice, device, queue, queueFamilyIndex] =
164         VK_ASSERT(SetUpTypicalVkTestEnvironment());
165 
166     const uint32_t width = 32;
167     const uint32_t height = 32;
168 
169     const vkhpp::ImageCreateInfo imageCreateInfo = {
170         .pNext = nullptr,
171         .imageType = vkhpp::ImageType::e2D,
172         .extent.width = width,
173         .extent.height = height,
174         .extent.depth = 1,
175         .mipLevels = 1,
176         .arrayLayers = 1,
177         .format = vkhpp::Format::eR8G8B8A8Unorm,
178         .tiling = vkhpp::ImageTiling::eOptimal,
179         .initialLayout = vkhpp::ImageLayout::eUndefined,
180         .usage = vkhpp::ImageUsageFlagBits::eSampled | vkhpp::ImageUsageFlagBits::eTransferDst |
181                  vkhpp::ImageUsageFlagBits::eTransferSrc,
182         .sharingMode = vkhpp::SharingMode::eExclusive,
183         .samples = vkhpp::SampleCountFlagBits::e1,
184     };
185     auto image = device->createImageUnique(imageCreateInfo).value;
186     ASSERT_THAT(image, IsValidHandle());
187 
188     vkhpp::MemoryRequirements imageMemoryRequirements{};
189     device->getImageMemoryRequirements(*image, &imageMemoryRequirements);
190 
191     const uint32_t imageMemoryIndex = utils::getMemoryType(
192         physicalDevice, imageMemoryRequirements, vkhpp::MemoryPropertyFlagBits::eDeviceLocal);
193     ASSERT_THAT(imageMemoryIndex, Not(Eq(-1)));
194 
195     const vkhpp::MemoryAllocateInfo imageMemoryAllocateInfo = {
196         .allocationSize = imageMemoryRequirements.size,
197         .memoryTypeIndex = imageMemoryIndex,
198     };
199 
200     auto imageMemory = device->allocateMemoryUnique(imageMemoryAllocateInfo).value;
201     ASSERT_THAT(imageMemory, IsValidHandle());
202 
203     ASSERT_THAT(device->bindImageMemory(*image, *imageMemory, 0), IsVkSuccess());
204 
205     // b/331677615
206     // Create and delete a buffer handle right before creating image view.
207     // Gfxstream recycle handles. We trick the VkImageView handle to collide with
208     // a destroyed buffer handle and verify there is no bug snapshotting recycled
209     // handles.
210     const vkhpp::BufferCreateInfo bufferCreateInfo = {
211         .size = 1024,
212         .usage = vkhpp::BufferUsageFlagBits::eTransferSrc,
213     };
214     auto buffer = device->createBufferUnique(bufferCreateInfo).value;
215     ASSERT_THAT(buffer, IsValidHandle());
216     buffer.reset();
217 
218     const vkhpp::ImageViewCreateInfo imageViewCreateInfo = {
219         .image = *image,
220         .viewType = vkhpp::ImageViewType::e2D,
221         .format = vkhpp::Format::eR8G8B8A8Unorm,
222         .subresourceRange =
223             {
224                 .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
225                 .baseMipLevel = 0,
226                 .levelCount = 1,
227                 .baseArrayLayer = 0,
228                 .layerCount = 1,
229             },
230     };
231     auto imageView = device->createImageViewUnique(imageViewCreateInfo).value;
232     ASSERT_THAT(imageView, IsValidHandle());
233     // Make sure it doesn't crash on load
234     SnapshotSaveAndLoad();
235 }
236 
TEST_P(GfxstreamEnd2EndVkSnapshotImageTest,MultiSampleImage)237 TEST_P(GfxstreamEnd2EndVkSnapshotImageTest, MultiSampleImage) {
238     auto [instance, physicalDevice, device, queue, queueFamilyIndex] =
239         VK_ASSERT(SetUpTypicalVkTestEnvironment());
240 
241     const uint32_t width = 32;
242     const uint32_t height = 32;
243 
244     const vkhpp::ImageCreateInfo imageCreateInfo = {
245         .pNext = nullptr,
246         .imageType = vkhpp::ImageType::e2D,
247         .extent.width = width,
248         .extent.height = height,
249         .extent.depth = 1,
250         .mipLevels = 1,
251         .arrayLayers = 1,
252         .format = vkhpp::Format::eR8G8B8A8Unorm,
253         .tiling = vkhpp::ImageTiling::eOptimal,
254         .initialLayout = vkhpp::ImageLayout::eUndefined,
255         .usage = vkhpp::ImageUsageFlagBits::eColorAttachment |
256                  vkhpp::ImageUsageFlagBits::eTransferDst | vkhpp::ImageUsageFlagBits::eTransferSrc,
257         .sharingMode = vkhpp::SharingMode::eExclusive,
258         .samples = vkhpp::SampleCountFlagBits::e8,
259     };
260     auto image = device->createImageUnique(imageCreateInfo).value;
261     ASSERT_THAT(image, IsValidHandle());
262 
263     vkhpp::MemoryRequirements imageMemoryRequirements{};
264     device->getImageMemoryRequirements(*image, &imageMemoryRequirements);
265 
266     const uint32_t imageMemoryIndex = utils::getMemoryType(
267         physicalDevice, imageMemoryRequirements, vkhpp::MemoryPropertyFlagBits::eDeviceLocal);
268     ASSERT_THAT(imageMemoryIndex, Not(Eq(-1)));
269 
270     const vkhpp::MemoryAllocateInfo imageMemoryAllocateInfo = {
271         .allocationSize = imageMemoryRequirements.size,
272         .memoryTypeIndex = imageMemoryIndex,
273     };
274 
275     auto imageMemory = device->allocateMemoryUnique(imageMemoryAllocateInfo).value;
276     ASSERT_THAT(imageMemory, IsValidHandle());
277 
278     // Make sure it doesn't crash on load
279     SnapshotSaveAndLoad();
280 }
281 
TEST_P(GfxstreamEnd2EndVkSnapshotImageTest,ImageViewDependencyWithDedicatedMemory)282 TEST_P(GfxstreamEnd2EndVkSnapshotImageTest, ImageViewDependencyWithDedicatedMemory) {
283     auto [instance, physicalDevice, device, queue, queueFamilyIndex] =
284         VK_ASSERT(SetUpTypicalVkTestEnvironment());
285 
286     const uint32_t width = 32;
287     const uint32_t height = 32;
288 
289     const vkhpp::ImageCreateInfo imageCreateInfo = {
290         .pNext = nullptr,
291         .imageType = vkhpp::ImageType::e2D,
292         .extent.width = width,
293         .extent.height = height,
294         .extent.depth = 1,
295         .mipLevels = 1,
296         .arrayLayers = 1,
297         .format = vkhpp::Format::eR8G8B8A8Unorm,
298         .tiling = vkhpp::ImageTiling::eOptimal,
299         .initialLayout = vkhpp::ImageLayout::eUndefined,
300         .usage = vkhpp::ImageUsageFlagBits::eSampled | vkhpp::ImageUsageFlagBits::eTransferDst |
301                  vkhpp::ImageUsageFlagBits::eTransferSrc,
302         .sharingMode = vkhpp::SharingMode::eExclusive,
303         .samples = vkhpp::SampleCountFlagBits::e1,
304     };
305     auto image = device->createImageUnique(imageCreateInfo).value;
306     ASSERT_THAT(image, IsValidHandle());
307 
308     vkhpp::MemoryRequirements imageMemoryRequirements{};
309     device->getImageMemoryRequirements(*image, &imageMemoryRequirements);
310 
311     const uint32_t imageMemoryIndex = utils::getMemoryType(
312         physicalDevice, imageMemoryRequirements, vkhpp::MemoryPropertyFlagBits::eDeviceLocal);
313     ASSERT_THAT(imageMemoryIndex, Not(Eq(-1)));
314 
315     const vkhpp::MemoryDedicatedAllocateInfo dedicatedAllocateInfo = {
316         .image = *image,
317     };
318 
319     const vkhpp::MemoryAllocateInfo imageMemoryAllocateInfo = {
320         .pNext = &dedicatedAllocateInfo,
321         .allocationSize = imageMemoryRequirements.size,
322         .memoryTypeIndex = imageMemoryIndex,
323     };
324 
325     auto imageMemory = device->allocateMemoryUnique(imageMemoryAllocateInfo).value;
326     ASSERT_THAT(imageMemory, IsValidHandle());
327 
328     ASSERT_THAT(device->bindImageMemory(*image, *imageMemory, 0), IsVkSuccess());
329 
330     const vkhpp::ImageViewCreateInfo imageViewCreateInfo = {
331         .image = *image,
332         .viewType = vkhpp::ImageViewType::e2D,
333         .format = vkhpp::Format::eR8G8B8A8Unorm,
334         .subresourceRange =
335             {
336                 .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
337                 .baseMipLevel = 0,
338                 .levelCount = 1,
339                 .baseArrayLayer = 0,
340                 .layerCount = 1,
341             },
342     };
343     auto imageView = device->createImageViewUnique(imageViewCreateInfo).value;
344     ASSERT_THAT(imageView, IsValidHandle());
345     // Make sure it doesn't crash on load
346     SnapshotSaveAndLoad();
347 }
348 
TEST_P(GfxstreamEnd2EndVkSnapshotImageTest,ImageContent)349 TEST_P(GfxstreamEnd2EndVkSnapshotImageTest, ImageContent) {
350     static constexpr int kWidth = 256;
351     static constexpr int kHeight = 256;
352     static constexpr vkhpp::DeviceSize kSize = 4 * kWidth * kHeight;
353 
354     std::vector<uint8_t> srcBufferContent(kSize);
355     for (size_t i = 0; i < kSize; i++) {
356         srcBufferContent[i] = static_cast<uint8_t>(i & 0xff);
357     }
358     TypicalVkTestEnvironment testEnvironment = VK_ASSERT(SetUpTypicalVkTestEnvironment());
359     auto& instance = testEnvironment.instance;
360     auto& physicalDevice = testEnvironment.physicalDevice;
361     auto& device = testEnvironment.device;
362     auto& queue = testEnvironment.queue;
363     auto queueFamilyIndex = testEnvironment.queueFamilyIndex;
364 
365     // Staging buffer
366     const vkhpp::BufferCreateInfo bufferCreateInfo = {
367         .size = static_cast<VkDeviceSize>(kSize),
368         .usage = vkhpp::BufferUsageFlagBits::eTransferSrc,
369         .sharingMode = vkhpp::SharingMode::eExclusive,
370     };
371     auto stagingBuffer = device->createBufferUnique(bufferCreateInfo).value;
372     ASSERT_THAT(stagingBuffer, IsValidHandle());
373 
374     vkhpp::MemoryRequirements stagingBufferMemoryRequirements{};
375     device->getBufferMemoryRequirements(*stagingBuffer, &stagingBufferMemoryRequirements);
376 
377     const auto stagingBufferMemoryType = utils::getMemoryType(
378         physicalDevice, stagingBufferMemoryRequirements,
379         vkhpp::MemoryPropertyFlagBits::eHostVisible | vkhpp::MemoryPropertyFlagBits::eHostCoherent);
380 
381     // Staging memory
382     const vkhpp::MemoryAllocateInfo stagingBufferMemoryAllocateInfo = {
383         .allocationSize = stagingBufferMemoryRequirements.size,
384         .memoryTypeIndex = stagingBufferMemoryType,
385     };
386     auto stagingBufferMemory = device->allocateMemoryUnique(stagingBufferMemoryAllocateInfo).value;
387     ASSERT_THAT(stagingBufferMemory, IsValidHandle());
388     ASSERT_THAT(device->bindBufferMemory(*stagingBuffer, *stagingBufferMemory, 0), IsVkSuccess());
389 
390     // Fill memory content
391     void* mapped = nullptr;
392     auto mapResult =
393         device->mapMemory(*stagingBufferMemory, 0, VK_WHOLE_SIZE, vkhpp::MemoryMapFlags{}, &mapped);
394     ASSERT_THAT(mapResult, IsVkSuccess());
395     ASSERT_THAT(mapped, NotNull());
396 
397     auto* bytes = reinterpret_cast<uint8_t*>(mapped);
398     std::memcpy(bytes, srcBufferContent.data(), kSize);
399 
400     const vkhpp::MappedMemoryRange range = {
401         .memory = *stagingBufferMemory,
402         .offset = 0,
403         .size = kSize,
404     };
405     device->unmapMemory(*stagingBufferMemory);
406 
407     // Image
408     const vkhpp::ImageCreateInfo imageCreateInfo = {
409         .pNext = nullptr,
410         .imageType = vkhpp::ImageType::e2D,
411         .extent.width = kWidth,
412         .extent.height = kHeight,
413         .extent.depth = 1,
414         .mipLevels = 1,
415         .arrayLayers = 1,
416         .format = vkhpp::Format::eR8G8B8A8Unorm,
417         .tiling = vkhpp::ImageTiling::eOptimal,
418         .initialLayout = vkhpp::ImageLayout::ePreinitialized,
419         .usage = vkhpp::ImageUsageFlagBits::eTransferDst | vkhpp::ImageUsageFlagBits::eTransferSrc,
420         .sharingMode = vkhpp::SharingMode::eExclusive,
421         .samples = vkhpp::SampleCountFlagBits::e1,
422     };
423     auto image = device->createImageUnique(imageCreateInfo).value;
424     ASSERT_THAT(image, IsValidHandle());
425 
426     vkhpp::MemoryRequirements imageMemoryRequirements{};
427     device->getImageMemoryRequirements(*image, &imageMemoryRequirements);
428 
429     const uint32_t imageMemoryIndex = utils::getMemoryType(
430         physicalDevice, imageMemoryRequirements, vkhpp::MemoryPropertyFlagBits::eDeviceLocal);
431     ASSERT_THAT(imageMemoryIndex, Not(Eq(-1)));
432 
433     const vkhpp::MemoryAllocateInfo imageMemoryAllocateInfo = {
434         .allocationSize = imageMemoryRequirements.size,
435         .memoryTypeIndex = imageMemoryIndex,
436     };
437 
438     auto imageMemory = device->allocateMemoryUnique(imageMemoryAllocateInfo).value;
439     ASSERT_THAT(imageMemory, IsValidHandle());
440 
441     ASSERT_THAT(device->bindImageMemory(*image, *imageMemory, 0), IsVkSuccess());
442 
443     // Command buffer
444     const vkhpp::CommandPoolCreateInfo commandPoolCreateInfo = {
445         .queueFamilyIndex = queueFamilyIndex,
446     };
447 
448     auto commandPool = device->createCommandPoolUnique(commandPoolCreateInfo).value;
449     ASSERT_THAT(commandPool, IsValidHandle());
450 
451     const vkhpp::CommandBufferAllocateInfo commandBufferAllocateInfo = {
452         .level = vkhpp::CommandBufferLevel::ePrimary,
453         .commandPool = *commandPool,
454         .commandBufferCount = 1,
455     };
456     auto commandBuffers = device->allocateCommandBuffersUnique(commandBufferAllocateInfo).value;
457     ASSERT_THAT(commandBuffers, Not(IsEmpty()));
458     auto commandBuffer = std::move(commandBuffers[0]);
459     ASSERT_THAT(commandBuffer, IsValidHandle());
460 
461     const vkhpp::CommandBufferBeginInfo commandBufferBeginInfo = {
462         .flags = vkhpp::CommandBufferUsageFlagBits::eOneTimeSubmit,
463     };
464     commandBuffer->begin(commandBufferBeginInfo);
465 
466     const vkhpp::ImageMemoryBarrier barrier{
467         .oldLayout = vkhpp::ImageLayout::eUndefined,
468         .newLayout = vkhpp::ImageLayout::eTransferDstOptimal,
469         .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
470         .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
471         .image = *image,
472         .subresourceRange =
473             {
474                 .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
475                 .levelCount = 1,
476                 .layerCount = 1,
477             },
478     };
479 
480     commandBuffer->pipelineBarrier(vkhpp::PipelineStageFlagBits::eAllCommands,
481                                    vkhpp::PipelineStageFlagBits::eAllCommands,
482                                    vkhpp::DependencyFlags(), nullptr, nullptr, barrier);
483 
484     const vkhpp::BufferImageCopy bufferImageCopy = {
485         .imageSubresource =
486             {
487                 .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
488                 .layerCount = 1,
489             },
490         .imageExtent =
491             {
492                 .width = kWidth,
493                 .height = kHeight,
494                 .depth = 1,
495             },
496     };
497     commandBuffer->copyBufferToImage(*stagingBuffer, *image,
498                                      vkhpp::ImageLayout::eTransferDstOptimal, 1, &bufferImageCopy);
499 
500     commandBuffer->end();
501 
502     auto transferFence = device->createFenceUnique(vkhpp::FenceCreateInfo()).value;
503     ASSERT_THAT(transferFence, IsValidHandle());
504 
505     // Execute the command to copy image
506     const vkhpp::SubmitInfo submitInfo = {
507         .commandBufferCount = 1,
508         .pCommandBuffers = &commandBuffer.get(),
509     };
510     queue.submit(submitInfo, *transferFence);
511 
512     auto waitResult = device->waitForFences(*transferFence, VK_TRUE, 3000000000L);
513     ASSERT_THAT(waitResult, IsVkSuccess());
514 
515     // Snapshot
516     SnapshotSaveAndLoad();
517 
518     std::vector<uint8_t> dst(kSize);
519     utils::readImageData(*image, kWidth, kHeight, vkhpp::ImageLayout::eTransferDstOptimal,
520                          dst.data(), kSize, testEnvironment);
521 
522     for (uint32_t i = 0; i < kSize; ++i) {
523         ASSERT_THAT(dst[i], Eq(srcBufferContent[i]));
524     }
525 }
526 
527 INSTANTIATE_TEST_CASE_P(GfxstreamEnd2EndTests, GfxstreamEnd2EndVkSnapshotImageTest,
528                         ::testing::ValuesIn({
529                             TestParams{
530                                 .with_gl = false,
531                                 .with_vk = true,
532                                 .with_features = {"VulkanSnapshots"},
533                             },
534                         }),
535                         &GetTestName);
536 
537 }  // namespace
538 }  // namespace tests
539 }  // namespace gfxstream