1 // Copyright (C) 2024 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 <android-base/expected.h>
16 
17 #include <string>
18 
19 #include "GfxstreamEnd2EndTestUtils.h"
20 #include "GfxstreamEnd2EndTests.h"
21 #include "gfxstream/RutabagaLayerTestUtils.h"
22 #include "simple_shader_frag.h"
23 #include "simple_shader_vert.h"
24 
25 namespace gfxstream {
26 namespace tests {
27 namespace {
28 
29 using testing::Eq;
30 using testing::Ge;
31 using testing::IsEmpty;
32 using testing::IsNull;
33 using testing::Not;
34 using testing::NotNull;
35 
36 struct PipelineInfo {
37     vkhpp::UniqueRenderPass renderPass;
38     vkhpp::UniqueDescriptorSetLayout descriptorSetLayout;
39     vkhpp::UniquePipelineLayout pipelineLayout;
40     vkhpp::UniqueShaderModule vertexShaderModule;
41     vkhpp::UniqueShaderModule fragmentShaderModule;
42     vkhpp::UniquePipeline pipeline;
43 };
44 
45 struct ImageInfo {
46     vkhpp::UniqueImage image;
47     vkhpp::UniqueDeviceMemory memory;
48     vkhpp::UniqueImageView imageView;
49 };
50 
51 struct BufferInfo {
52     vkhpp::UniqueBuffer buffer;
53     vkhpp::UniqueDeviceMemory memory;
54 };
55 
56 class GfxstreamEnd2EndVkSnapshotPipelineTest : public GfxstreamEnd2EndTest {
57    protected:
58     vkhpp::UniqueRenderPass createRenderPass(vkhpp::Device device);
59     std::unique_ptr<ImageInfo> createColorAttachment(vkhpp::PhysicalDevice physicalDevice,
60                                                      vkhpp::Device device);
61     std::unique_ptr<PipelineInfo> createPipeline(vkhpp::Device device);
62     VkExpected<BufferInfo> createAndPopulateBuffer(vkhpp::PhysicalDevice physicalDevice,
63                                                    vkhpp::Device device,
64                                                    vkhpp::BufferUsageFlags usage, const void* data,
65                                                    uint64_t dataSize);
66     static const uint32_t kFbWidth = 32;
67     static const uint32_t kFbHeight = 32;
68 };
69 
70 class GfxstreamEnd2EndVkSnapshotPipelineWithMultiSamplingTest
71     : public GfxstreamEnd2EndVkSnapshotPipelineTest {};
72 
73 template <typename DurationType>
AsVkTimeout(DurationType duration)74 constexpr uint64_t AsVkTimeout(DurationType duration) {
75     return static_cast<uint64_t>(
76         std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count());
77 }
78 
79 // Full screen blue rectangle
80 const float kFullscreenBlueRectangleVertexData[] = {
81     // clang-format off
82     /*pos=*/ -1.0f, -1.0f, 0.0f, 1.0f, /*color=*/ 0.0f,  0.0f,  1.0f, 1.0f,
83     /*pos=*/  1.0f, -1.0f, 0.0f, 1.0f, /*color=*/ 0.0f,  0.0f,  1.0f, 1.0f,
84     /*pos=*/  1.0f,  1.0f, 0.0f, 1.0f, /*color=*/ 0.0f,  0.0f,  1.0f, 1.0f,
85     /*pos=*/  1.0f,  1.0f, 0.0f, 1.0f, /*color=*/ 0.0f,  0.0f,  1.0f, 1.0f,
86     /*pos=*/ -1.0f,  1.0f, 0.0f, 1.0f, /*color=*/ 0.0f,  0.0f,  1.0f, 1.0f,
87     /*pos=*/ -1.0f, -1.0f, 0.0f, 1.0f, /*color=*/ 0.0f,  0.0f,  1.0f, 1.0f,
88     // clang-format on
89 };
90 
createAndPopulateBuffer(vkhpp::PhysicalDevice physicalDevice,vkhpp::Device device,vkhpp::BufferUsageFlags usage,const void * data,uint64_t dataSize)91 VkExpected<BufferInfo> GfxstreamEnd2EndVkSnapshotPipelineTest::createAndPopulateBuffer(
92     vkhpp::PhysicalDevice physicalDevice, vkhpp::Device device, vkhpp::BufferUsageFlags usage,
93     const void* data, uint64_t dataSize) {
94     const vkhpp::BufferCreateInfo vertexBufferCreateInfo = {
95         .size = dataSize,
96         .usage = usage,
97         .sharingMode = vkhpp::SharingMode::eExclusive,
98     };
99     auto vertexBuffer = VK_EXPECT_RV(device.createBufferUnique(vertexBufferCreateInfo));
100 
101     vkhpp::MemoryRequirements vertexBufferMemoryRequirements{};
102     device.getBufferMemoryRequirements(*vertexBuffer, &vertexBufferMemoryRequirements);
103 
104     const auto vertexBufferMemoryType = utils::getMemoryType(
105         physicalDevice, vertexBufferMemoryRequirements,
106         vkhpp::MemoryPropertyFlagBits::eHostVisible | vkhpp::MemoryPropertyFlagBits::eHostCoherent);
107     if (vertexBufferMemoryType == -1) {
108         return android::base::unexpected(vkhpp::Result::eErrorOutOfHostMemory);
109     }
110     // Vertex memory
111     const vkhpp::MemoryAllocateInfo vertexBufferMemoryAllocateInfo = {
112         .allocationSize = vertexBufferMemoryRequirements.size,
113         .memoryTypeIndex = vertexBufferMemoryType,
114     };
115     auto vertexBufferMemory =
116         VK_EXPECT_RV(device.allocateMemoryUnique(vertexBufferMemoryAllocateInfo));
117     device.bindBufferMemory(*vertexBuffer, *vertexBufferMemory, 0);
118     void* mapped;
119     device.mapMemory(*vertexBufferMemory, 0, VK_WHOLE_SIZE, vkhpp::MemoryMapFlags{}, &mapped);
120     memcpy(mapped, data, dataSize);
121     device.unmapMemory(*vertexBufferMemory);
122 
123     BufferInfo res;
124     res.buffer = std::move(vertexBuffer);
125     res.memory = std::move(vertexBufferMemory);
126     return res;
127 }
128 
createRenderPass(vkhpp::Device device)129 vkhpp::UniqueRenderPass GfxstreamEnd2EndVkSnapshotPipelineTest::createRenderPass(
130     vkhpp::Device device) {
131     vkhpp::AttachmentDescription colorAttachmentDescription = {
132         .format = vkhpp::Format::eR8G8B8A8Unorm,
133         .samples = static_cast<vkhpp::SampleCountFlagBits>(GetParam().samples),
134         .loadOp = vkhpp::AttachmentLoadOp::eLoad,
135         .storeOp = vkhpp::AttachmentStoreOp::eStore,
136         .initialLayout = vkhpp::ImageLayout::eColorAttachmentOptimal,
137         .finalLayout = vkhpp::ImageLayout::eColorAttachmentOptimal,
138     };
139     vkhpp::AttachmentReference attachmentReference = {
140         .attachment = 0,
141         .layout = vkhpp::ImageLayout::eColorAttachmentOptimal,
142     };
143     vkhpp::SubpassDescription subpassDescription = {
144         .colorAttachmentCount = 1,
145         .pColorAttachments = &attachmentReference,
146     };
147     vkhpp::RenderPassCreateInfo renderPassCreateInfo = {
148         .attachmentCount = 1,
149         .pAttachments = &colorAttachmentDescription,
150         .subpassCount = 1,
151         .pSubpasses = &subpassDescription,
152     };
153     return device.createRenderPassUnique(renderPassCreateInfo).value;
154 }
155 
createPipeline(vkhpp::Device device)156 std::unique_ptr<PipelineInfo> GfxstreamEnd2EndVkSnapshotPipelineTest::createPipeline(
157     vkhpp::Device device) {
158     std::unique_ptr<PipelineInfo> res(new PipelineInfo);
159     res->renderPass = createRenderPass(device);
160 
161     vkhpp::DescriptorSetLayoutBinding bindings[1] = {
162         {
163             .binding = 0,
164             .descriptorType = vkhpp::DescriptorType::eUniformBuffer,
165             .descriptorCount = 1,
166             .stageFlags = vkhpp::ShaderStageFlagBits::eFragment,
167         },
168     };
169     vkhpp::DescriptorSetLayoutCreateInfo descriptorSetLayoutInfo = {
170         .bindingCount = 1,
171         .pBindings = bindings,
172     };
173     res->descriptorSetLayout =
174         device.createDescriptorSetLayoutUnique(descriptorSetLayoutInfo).value;
175     res->pipelineLayout = device
176                               .createPipelineLayoutUnique(vkhpp::PipelineLayoutCreateInfo{
177                                   .setLayoutCount = 1,
178                                   .pSetLayouts = &res->descriptorSetLayout.get(),
179                               })
180                               .value;
181 
182     vkhpp::ShaderModuleCreateInfo vertexShaderModuleCreateInfo = {
183         .codeSize = sizeof(kSimpleShaderVert),
184         .pCode = (const uint32_t*)kSimpleShaderVert,
185     };
186     vkhpp::ShaderModuleCreateInfo fragmentShaderModuleCreateInfo = {
187         .codeSize = sizeof(kSimpleShaderFrag),
188         .pCode = (const uint32_t*)kSimpleShaderFrag,
189     };
190     res->vertexShaderModule = device.createShaderModuleUnique(vertexShaderModuleCreateInfo).value;
191     res->fragmentShaderModule =
192         device.createShaderModuleUnique(fragmentShaderModuleCreateInfo).value;
193 
194     vkhpp::PipelineShaderStageCreateInfo pipelineShaderStageCreateInfos[2] = {
195         {
196             .stage = vkhpp::ShaderStageFlagBits::eVertex,
197             .module = *(res->vertexShaderModule),
198             .pName = "main",
199         },
200         {
201             .stage = vkhpp::ShaderStageFlagBits::eFragment,
202             .module = *(res->fragmentShaderModule),
203             .pName = "main",
204         },
205     };
206 
207     const vkhpp::VertexInputBindingDescription vertexInputBindingDescription = {
208         .stride = 32,
209     };
210     vkhpp::VertexInputAttributeDescription vertexInputAttributeDescriptions[2] = {
211         {
212             .location = 0,
213             .format = vkhpp::Format::eR32G32B32A32Sfloat,
214             .offset = 0,
215         },
216         {
217             .location = 1,
218             .format = vkhpp::Format::eR32G32B32A32Sfloat,
219             .offset = 16,
220         },
221     };
222     const vkhpp::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo = {
223         .vertexBindingDescriptionCount = 1,
224         .pVertexBindingDescriptions = &vertexInputBindingDescription,
225         .vertexAttributeDescriptionCount = 2,
226         .pVertexAttributeDescriptions = vertexInputAttributeDescriptions,
227     };
228 
229     const vkhpp::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo = {
230         .topology = vkhpp::PrimitiveTopology::eTriangleList,
231     };
232 
233     const vkhpp::PipelineViewportStateCreateInfo pipelineViewportStateCreateInfo = {
234         .viewportCount = 1,
235         .scissorCount = 1,
236     };
237 
238     const vkhpp::PipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo = {
239         .cullMode = vkhpp::CullModeFlagBits::eNone,
240         .lineWidth = 1.0f,
241     };
242 
243     const vkhpp::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo = {
244         .rasterizationSamples = static_cast<vkhpp::SampleCountFlagBits>(GetParam().samples),
245     };
246     const vkhpp::PipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo = {};
247     const vkhpp::PipelineColorBlendAttachmentState pipelineColorBlendAttachmentState = {
248         .colorBlendOp = vkhpp::BlendOp::eAdd,
249         .srcAlphaBlendFactor = vkhpp::BlendFactor::eZero,
250         .dstAlphaBlendFactor = vkhpp::BlendFactor::eZero,
251         .alphaBlendOp = vkhpp::BlendOp::eAdd,
252         .colorWriteMask = vkhpp::ColorComponentFlagBits::eR | vkhpp::ColorComponentFlagBits::eG |
253                           vkhpp::ColorComponentFlagBits::eB | vkhpp::ColorComponentFlagBits::eA};
254     const vkhpp::PipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo = {
255         .attachmentCount = 1,
256         .pAttachments = &pipelineColorBlendAttachmentState,
257     };
258     const vkhpp::DynamicState dynamicStates[2] = {vkhpp::DynamicState::eViewport,
259                                                   vkhpp::DynamicState::eScissor};
260     const vkhpp::PipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo = {
261         .dynamicStateCount = 2,
262         .pDynamicStates = dynamicStates,
263     };
264 
265     vkhpp::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo = {
266         .stageCount = 2,
267         .pStages = pipelineShaderStageCreateInfos,
268         .pVertexInputState = &pipelineVertexInputStateCreateInfo,
269         .pInputAssemblyState = &pipelineInputAssemblyStateCreateInfo,
270         .pViewportState = &pipelineViewportStateCreateInfo,
271         .pRasterizationState = &pipelineRasterizationStateCreateInfo,
272         .pMultisampleState = &pipelineMultisampleStateCreateInfo,
273         .pDepthStencilState = &pipelineDepthStencilStateCreateInfo,
274         .pColorBlendState = &pipelineColorBlendStateCreateInfo,
275         .pDynamicState = &pipelineDynamicStateCreateInfo,
276         .layout = *(res->pipelineLayout),
277         .renderPass = *(res->renderPass),
278     };
279 
280     res->pipeline = device.createGraphicsPipelineUnique(nullptr, graphicsPipelineCreateInfo).value;
281 
282     return res;
283 }
284 
createColorAttachment(vkhpp::PhysicalDevice physicalDevice,vkhpp::Device device)285 std::unique_ptr<ImageInfo> GfxstreamEnd2EndVkSnapshotPipelineTest::createColorAttachment(
286     vkhpp::PhysicalDevice physicalDevice, vkhpp::Device device) {
287     std::unique_ptr<ImageInfo> res(new ImageInfo);
288 
289     const vkhpp::ImageCreateInfo imageCreateInfo = {
290         .pNext = nullptr,
291         .imageType = vkhpp::ImageType::e2D,
292         .extent.width = kFbWidth,
293         .extent.height = kFbHeight,
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::eColorAttachment | vkhpp::ImageUsageFlagBits::eSampled |
301                  vkhpp::ImageUsageFlagBits::eTransferDst | vkhpp::ImageUsageFlagBits::eTransferSrc,
302         .sharingMode = vkhpp::SharingMode::eExclusive,
303         .samples = static_cast<vkhpp::SampleCountFlagBits>(GetParam().samples),
304     };
305     res->image = device.createImageUnique(imageCreateInfo).value;
306 
307     vkhpp::MemoryRequirements imageMemoryRequirements{};
308     device.getImageMemoryRequirements(*(res->image), &imageMemoryRequirements);
309 
310     const uint32_t imageMemoryIndex = utils::getMemoryType(
311         physicalDevice, imageMemoryRequirements, vkhpp::MemoryPropertyFlagBits::eDeviceLocal);
312 
313     const vkhpp::MemoryAllocateInfo imageMemoryAllocateInfo = {
314         .allocationSize = imageMemoryRequirements.size,
315         .memoryTypeIndex = imageMemoryIndex,
316     };
317 
318     res->memory = device.allocateMemoryUnique(imageMemoryAllocateInfo).value;
319 
320     device.bindImageMemory(*(res->image), *(res->memory), 0);
321 
322     const vkhpp::ImageViewCreateInfo imageViewCreateInfo = {
323         .image = *(res->image),
324         .viewType = vkhpp::ImageViewType::e2D,
325         .format = vkhpp::Format::eR8G8B8A8Unorm,
326         .subresourceRange =
327             {
328                 .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
329                 .baseMipLevel = 0,
330                 .levelCount = 1,
331                 .baseArrayLayer = 0,
332                 .layerCount = 1,
333             },
334     };
335     res->imageView = device.createImageViewUnique(imageViewCreateInfo).value;
336     return res;
337 }
338 
TEST_P(GfxstreamEnd2EndVkSnapshotPipelineTest,CanRecreateShaderModule)339 TEST_P(GfxstreamEnd2EndVkSnapshotPipelineTest, CanRecreateShaderModule) {
340     auto [instance, physicalDevice, device, queue, queueFamilyIndex] =
341         VK_ASSERT(SetUpTypicalVkTestEnvironment());
342     auto pipelineInfo = createPipeline(device.get());
343     ASSERT_THAT(pipelineInfo->renderPass, IsValidHandle());
344     ASSERT_THAT(pipelineInfo->descriptorSetLayout, IsValidHandle());
345     ASSERT_THAT(pipelineInfo->pipelineLayout, IsValidHandle());
346     ASSERT_THAT(pipelineInfo->vertexShaderModule, IsValidHandle());
347     ASSERT_THAT(pipelineInfo->fragmentShaderModule, IsValidHandle());
348     ASSERT_THAT(pipelineInfo->pipeline, IsValidHandle());
349 
350     // Check if snapshot can restore the pipeline even after shaders are destroyed.
351     pipelineInfo->vertexShaderModule.reset();
352     pipelineInfo->fragmentShaderModule.reset();
353 
354     SnapshotSaveAndLoad();
355     // Don't crash
356     // TODO(b/330763497): try to render something
357     // TODO(b/330766521): fix dangling shader modules after snapshot load
358 }
359 
360 // vkCreateDescriptorPool injects extra handles into the internal handle map, thus add
361 // a test for it.
TEST_P(GfxstreamEnd2EndVkSnapshotPipelineTest,CanSnapshotDescriptorPool)362 TEST_P(GfxstreamEnd2EndVkSnapshotPipelineTest, CanSnapshotDescriptorPool) {
363     auto [instance, physicalDevice, device, queue, queueFamilyIndex] =
364         VK_ASSERT(SetUpTypicalVkTestEnvironment());
365     std::vector<vkhpp::DescriptorPoolSize> sizes = {
366         {
367             .descriptorCount = 10,
368         },
369     };
370     vkhpp::DescriptorPoolCreateInfo descriptorPoolCreateInfo = {
371         .maxSets = 10,
372         .poolSizeCount = static_cast<uint32_t>(sizes.size()),
373         .pPoolSizes = sizes.data(),
374     };
375     auto descriptorPool0 = device->createDescriptorPoolUnique(descriptorPoolCreateInfo).value;
376     ASSERT_THAT(descriptorPool0, IsValidHandle());
377     auto descriptorPool1 = device->createDescriptorPoolUnique(descriptorPoolCreateInfo).value;
378     ASSERT_THAT(descriptorPool1, IsValidHandle());
379 
380     vkhpp::DescriptorSetLayoutCreateInfo descriptorSetLayoutInfo = {};
381     auto descriptorSetLayout =
382         device->createDescriptorSetLayoutUnique(descriptorSetLayoutInfo).value;
383     ASSERT_THAT(descriptorSetLayout, IsValidHandle());
384 
385     SnapshotSaveAndLoad();
386 
387     const std::vector<vkhpp::DescriptorSetLayout> descriptorSetLayouts(1, *descriptorSetLayout);
388 
389     vkhpp::DescriptorSetAllocateInfo descriptorSetAllocateInfo0 = {
390         .descriptorPool = *descriptorPool0,
391         .descriptorSetCount = 1,
392         .pSetLayouts = descriptorSetLayouts.data(),
393     };
394     auto descriptorSets0 = device->allocateDescriptorSetsUnique(descriptorSetAllocateInfo0);
395     EXPECT_THAT(descriptorSets0.result, Eq(vkhpp::Result::eSuccess));
396 
397     vkhpp::DescriptorSetAllocateInfo descriptorSetAllocateInfo1 = {
398         .descriptorPool = *descriptorPool1,
399         .descriptorSetCount = 1,
400         .pSetLayouts = descriptorSetLayouts.data(),
401     };
402     auto descriptorSets1 = device->allocateDescriptorSetsUnique(descriptorSetAllocateInfo1);
403     EXPECT_THAT(descriptorSets1.result, Eq(vkhpp::Result::eSuccess));
404 }
405 
TEST_P(GfxstreamEnd2EndVkSnapshotPipelineTest,CanSnapshotFramebuffer)406 TEST_P(GfxstreamEnd2EndVkSnapshotPipelineTest, CanSnapshotFramebuffer) {
407     auto [instance, physicalDevice, device, queue, queueFamilyIndex] =
408         VK_ASSERT(SetUpTypicalVkTestEnvironment());
409     auto renderPass = createRenderPass(device.get());
410     ASSERT_THAT(renderPass, IsValidHandle());
411 
412     auto colorAttachmentInfo = createColorAttachment(physicalDevice, device.get());
413     ASSERT_THAT(colorAttachmentInfo->image, IsValidHandle());
414     ASSERT_THAT(colorAttachmentInfo->memory, IsValidHandle());
415     ASSERT_THAT(colorAttachmentInfo->imageView, IsValidHandle());
416 
417     const std::vector<vkhpp::ImageView> attachments(1, *colorAttachmentInfo->imageView);
418     vkhpp::FramebufferCreateInfo framebufferCreateInfo = {
419         .renderPass = *renderPass,
420         .attachmentCount = 1,
421         .pAttachments = attachments.data(),
422         .width = kFbWidth,
423         .height = kFbHeight,
424         .layers = 1,
425     };
426     auto framebuffer = device->createFramebufferUnique(framebufferCreateInfo).value;
427     ASSERT_THAT(framebuffer, IsValidHandle());
428 
429     SnapshotSaveAndLoad();
430 }
431 
TEST_P(GfxstreamEnd2EndVkSnapshotPipelineWithMultiSamplingTest,CanSubmitQueue)432 TEST_P(GfxstreamEnd2EndVkSnapshotPipelineWithMultiSamplingTest, CanSubmitQueue) {
433     TypicalVkTestEnvironment testEnvironment = VK_ASSERT(SetUpTypicalVkTestEnvironment());
434     auto& [instance, physicalDevice, device, queue, queueFamilyIndex] = testEnvironment;
435 
436     auto pipelineInfo = createPipeline(device.get());
437 
438     auto colorAttachmentInfo = createColorAttachment(physicalDevice, device.get());
439     ASSERT_THAT(colorAttachmentInfo->image, IsValidHandle());
440     ASSERT_THAT(colorAttachmentInfo->memory, IsValidHandle());
441     ASSERT_THAT(colorAttachmentInfo->imageView, IsValidHandle());
442 
443     const std::vector<vkhpp::ImageView> attachments(1, *colorAttachmentInfo->imageView);
444     vkhpp::FramebufferCreateInfo framebufferCreateInfo = {
445         .renderPass = *pipelineInfo->renderPass,
446         .attachmentCount = 1,
447         .pAttachments = attachments.data(),
448         .width = kFbWidth,
449         .height = kFbHeight,
450         .layers = 1,
451     };
452     auto framebuffer = device->createFramebufferUnique(framebufferCreateInfo).value;
453     ASSERT_THAT(framebuffer, IsValidHandle());
454 
455     auto fence = device->createFenceUnique(vkhpp::FenceCreateInfo()).value;
456     ASSERT_THAT(fence, IsValidHandle());
457 
458     const vkhpp::CommandPoolCreateInfo commandPoolCreateInfo = {
459         .flags = vkhpp::CommandPoolCreateFlagBits::eResetCommandBuffer,
460         .queueFamilyIndex = queueFamilyIndex,
461     };
462 
463     auto commandPool = device->createCommandPoolUnique(commandPoolCreateInfo).value;
464     ASSERT_THAT(commandPool, IsValidHandle());
465 
466     const vkhpp::CommandBufferAllocateInfo commandBufferAllocateInfo = {
467         .level = vkhpp::CommandBufferLevel::ePrimary,
468         .commandPool = *commandPool,
469         .commandBufferCount = 1,
470     };
471 
472     auto commandBuffers = device->allocateCommandBuffersUnique(commandBufferAllocateInfo).value;
473     ASSERT_THAT(commandBuffers, Not(IsEmpty()));
474     auto commandBuffer = std::move(commandBuffers[0]);
475     ASSERT_THAT(commandBuffer, IsValidHandle());
476 
477     vkhpp::ClearColorValue clearColor(std::array<float, 4>{1.0f, 0.0f, 1.0f, 1.0f});
478     vkhpp::ClearValue clearValue{
479         .color = clearColor,
480     };
481     vkhpp::RenderPassBeginInfo renderPassBeginInfo{
482         .renderPass = *pipelineInfo->renderPass,
483         .framebuffer = *framebuffer,
484         .renderArea = vkhpp::Rect2D(vkhpp::Offset2D(0, 0), vkhpp::Extent2D(kFbWidth, kFbHeight)),
485         .clearValueCount = 1,
486         .pClearValues = &clearValue,
487     };
488 
489     const vkhpp::CommandBufferBeginInfo commandBufferBeginInfo = {
490         .flags = vkhpp::CommandBufferUsageFlagBits::eOneTimeSubmit,
491     };
492 
493     commandBuffer->begin(commandBufferBeginInfo);
494     const vkhpp::ImageMemoryBarrier colorAttachmentBarrier{
495         .oldLayout = vkhpp::ImageLayout::eUndefined,
496         .newLayout = vkhpp::ImageLayout::eColorAttachmentOptimal,
497         .dstAccessMask = vkhpp::AccessFlagBits::eColorAttachmentRead |
498                          vkhpp::AccessFlagBits::eColorAttachmentWrite,
499         .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
500         .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
501         .image = *colorAttachmentInfo->image,
502         .subresourceRange =
503             {
504                 .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
505                 .levelCount = 1,
506                 .layerCount = 1,
507             },
508     };
509 
510     commandBuffer->pipelineBarrier(
511         vkhpp::PipelineStageFlagBits::eTopOfPipe | vkhpp::PipelineStageFlagBits::eTransfer,
512         vkhpp::PipelineStageFlagBits::eColorAttachmentOutput, vkhpp::DependencyFlags(), nullptr,
513         nullptr, colorAttachmentBarrier);
514 
515     commandBuffer->end();
516 
517     std::vector<vkhpp::CommandBuffer> commandBufferHandles;
518     commandBufferHandles.push_back(*commandBuffer);
519 
520     const vkhpp::SubmitInfo submitInfo = {
521         .commandBufferCount = static_cast<uint32_t>(commandBufferHandles.size()),
522         .pCommandBuffers = commandBufferHandles.data(),
523     };
524     queue.submit(submitInfo, *fence);
525 
526     auto waitResult = device->waitForFences(*fence, VK_TRUE, 3000000000L);
527     ASSERT_THAT(waitResult, IsVkSuccess());
528     commandBuffer->reset();
529 
530     SnapshotSaveAndLoad();
531     // TODO(b/332763326): fix validation layer complain about unreleased pipeline layout
532 
533     // Try to draw something.
534     // Color attachment layout must be snapshotted, otherwise validation layer will complain.
535     commandBuffer->begin(commandBufferBeginInfo);
536     commandBuffer->beginRenderPass(renderPassBeginInfo, vkhpp::SubpassContents::eInline);
537     commandBuffer->bindPipeline(vkhpp::PipelineBindPoint::eGraphics, *pipelineInfo->pipeline);
538 
539     vkhpp::ClearAttachment clearAttachment{
540         .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
541         .colorAttachment = 0,
542         .clearValue = clearValue,
543     };
544     vkhpp::ClearRect clearRect{
545         .rect = vkhpp::Rect2D(vkhpp::Offset2D(0, 0), vkhpp::Extent2D(kFbWidth, kFbHeight)),
546         .baseArrayLayer = 0,
547         .layerCount = 1,
548     };
549 
550     commandBuffer->clearAttachments(1, &clearAttachment, 1, &clearRect);
551     commandBuffer->endRenderPass();
552     commandBuffer->end();
553     queue.submit(submitInfo, *fence);
554 
555     waitResult = device->waitForFences(*fence, VK_TRUE, 3000000000L);
556     ASSERT_THAT(waitResult, IsVkSuccess());
557 
558     if (GetParam().samples != 1) {
559         return;
560     }
561 
562     std::vector<uint32_t> dst(kFbWidth * kFbHeight);
563     utils::readImageData(*colorAttachmentInfo->image, kFbWidth, kFbHeight,
564                          vkhpp::ImageLayout::eColorAttachmentOptimal, dst.data(),
565                          dst.size() * sizeof(uint32_t), testEnvironment);
566     for (int i = 0; i < dst.size(); i++) {
567         ASSERT_THAT(dst[i], Eq(0xffff00ff));
568     }
569 }
570 
TEST_P(GfxstreamEnd2EndVkSnapshotPipelineTest,CanSnapshotCommandBuffer)571 TEST_P(GfxstreamEnd2EndVkSnapshotPipelineTest, CanSnapshotCommandBuffer) {
572     TypicalVkTestEnvironment testEnvironment = VK_ASSERT(SetUpTypicalVkTestEnvironment());
573     auto& [instance, physicalDevice, device, queue, queueFamilyIndex] = testEnvironment;
574 
575     auto pipelineInfo = createPipeline(device.get());
576 
577     auto colorAttachmentInfo = createColorAttachment(physicalDevice, device.get());
578     ASSERT_THAT(colorAttachmentInfo->image, IsValidHandle());
579     ASSERT_THAT(colorAttachmentInfo->memory, IsValidHandle());
580     ASSERT_THAT(colorAttachmentInfo->imageView, IsValidHandle());
581 
582     const std::vector<vkhpp::ImageView> attachments(1, *colorAttachmentInfo->imageView);
583     vkhpp::FramebufferCreateInfo framebufferCreateInfo = {
584         .renderPass = *pipelineInfo->renderPass,
585         .attachmentCount = 1,
586         .pAttachments = attachments.data(),
587         .width = kFbWidth,
588         .height = kFbHeight,
589         .layers = 1,
590     };
591     auto framebuffer = device->createFramebufferUnique(framebufferCreateInfo).value;
592     ASSERT_THAT(framebuffer, IsValidHandle());
593 
594     auto fence = device->createFenceUnique(vkhpp::FenceCreateInfo()).value;
595     ASSERT_THAT(fence, IsValidHandle());
596 
597     const vkhpp::CommandPoolCreateInfo commandPoolCreateInfo = {
598         .flags = vkhpp::CommandPoolCreateFlagBits::eResetCommandBuffer,
599         .queueFamilyIndex = queueFamilyIndex,
600     };
601 
602     auto commandPool = device->createCommandPoolUnique(commandPoolCreateInfo).value;
603     ASSERT_THAT(commandPool, IsValidHandle());
604 
605     const vkhpp::CommandBufferAllocateInfo commandBufferAllocateInfo = {
606         .level = vkhpp::CommandBufferLevel::ePrimary,
607         .commandPool = *commandPool,
608         .commandBufferCount = 1,
609     };
610 
611     auto commandBuffers = device->allocateCommandBuffersUnique(commandBufferAllocateInfo).value;
612     ASSERT_THAT(commandBuffers, Not(IsEmpty()));
613     auto commandBuffer = std::move(commandBuffers[0]);
614     ASSERT_THAT(commandBuffer, IsValidHandle());
615 
616     vkhpp::ClearColorValue clearColor(std::array<float, 4>{1.0f, 0.0f, 1.0f, 1.0f});
617     vkhpp::ClearValue clearValue{
618         .color = clearColor,
619     };
620     vkhpp::RenderPassBeginInfo renderPassBeginInfo{
621         .renderPass = *pipelineInfo->renderPass,
622         .framebuffer = *framebuffer,
623         .renderArea = vkhpp::Rect2D(vkhpp::Offset2D(0, 0), vkhpp::Extent2D(kFbWidth, kFbHeight)),
624         .clearValueCount = 1,
625         .pClearValues = &clearValue,
626     };
627 
628     const vkhpp::CommandBufferBeginInfo commandBufferBeginInfo = {
629         .flags = vkhpp::CommandBufferUsageFlagBits::eOneTimeSubmit,
630     };
631 
632     commandBuffer->begin(commandBufferBeginInfo);
633     const vkhpp::ImageMemoryBarrier colorAttachmentBarrier{
634         .oldLayout = vkhpp::ImageLayout::eUndefined,
635         .newLayout = vkhpp::ImageLayout::eColorAttachmentOptimal,
636         .dstAccessMask = vkhpp::AccessFlagBits::eColorAttachmentRead |
637                          vkhpp::AccessFlagBits::eColorAttachmentWrite,
638         .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
639         .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
640         .image = *colorAttachmentInfo->image,
641         .subresourceRange =
642             {
643                 .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
644                 .levelCount = 1,
645                 .layerCount = 1,
646             },
647     };
648 
649     commandBuffer->pipelineBarrier(
650         vkhpp::PipelineStageFlagBits::eTopOfPipe | vkhpp::PipelineStageFlagBits::eTransfer,
651         vkhpp::PipelineStageFlagBits::eColorAttachmentOutput, vkhpp::DependencyFlags(), nullptr,
652         nullptr, colorAttachmentBarrier);
653 
654     commandBuffer->beginRenderPass(renderPassBeginInfo, vkhpp::SubpassContents::eInline);
655     commandBuffer->bindPipeline(vkhpp::PipelineBindPoint::eGraphics, *pipelineInfo->pipeline);
656 
657     vkhpp::ClearAttachment clearAttachment{
658         .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
659         .colorAttachment = 0,
660         .clearValue = clearValue,
661     };
662     vkhpp::ClearRect clearRect{
663         .rect = vkhpp::Rect2D(vkhpp::Offset2D(0, 0), vkhpp::Extent2D(kFbWidth, kFbHeight)),
664         .baseArrayLayer = 0,
665         .layerCount = 1,
666     };
667 
668     commandBuffer->clearAttachments(1, &clearAttachment, 1, &clearRect);
669     commandBuffer->endRenderPass();
670     commandBuffer->end();
671 
672     std::vector<vkhpp::CommandBuffer> commandBufferHandles;
673     commandBufferHandles.push_back(*commandBuffer);
674     const vkhpp::SubmitInfo submitInfo = {
675         .commandBufferCount = static_cast<uint32_t>(commandBufferHandles.size()),
676         .pCommandBuffers = commandBufferHandles.data(),
677     };
678 
679     SnapshotSaveAndLoad();
680     // Command buffer should still work after snapshot.
681 
682     queue.submit(submitInfo, *fence);
683 
684     auto waitResult = device->waitForFences(*fence, VK_TRUE, 3000000000L);
685     ASSERT_THAT(waitResult, IsVkSuccess());
686 
687     std::vector<uint32_t> dst(kFbWidth * kFbHeight);
688     utils::readImageData(*colorAttachmentInfo->image, kFbWidth, kFbHeight,
689                          vkhpp::ImageLayout::eColorAttachmentOptimal, dst.data(),
690                          dst.size() * sizeof(uint32_t), testEnvironment);
691     for (int i = 0; i < dst.size(); i++) {
692         ASSERT_THAT(dst[i], Eq(0xffff00ff));
693     }
694 }
695 
TEST_P(GfxstreamEnd2EndVkSnapshotPipelineTest,CanSnapshotDescriptors)696 TEST_P(GfxstreamEnd2EndVkSnapshotPipelineTest, CanSnapshotDescriptors) {
697     TypicalVkTestEnvironment testEnvironment = VK_ASSERT(SetUpTypicalVkTestEnvironment());
698     auto& [instance, physicalDevice, device, queue, queueFamilyIndex] = testEnvironment;
699 
700     auto pipelineInfo = createPipeline(device.get());
701     auto vertexBufferInfo = createAndPopulateBuffer(
702         physicalDevice, device.get(), vkhpp::BufferUsageFlagBits::eVertexBuffer,
703         kFullscreenBlueRectangleVertexData, sizeof(kFullscreenBlueRectangleVertexData));
704 
705     auto colorAttachmentInfo = createColorAttachment(physicalDevice, device.get());
706     ASSERT_THAT(colorAttachmentInfo->image, IsValidHandle());
707     ASSERT_THAT(colorAttachmentInfo->memory, IsValidHandle());
708     ASSERT_THAT(colorAttachmentInfo->imageView, IsValidHandle());
709 
710     // Descriptor
711     std::vector<vkhpp::DescriptorPoolSize> sizes = {
712         {
713             .type = vkhpp::DescriptorType::eUniformBuffer,
714             .descriptorCount = 10,
715         },
716     };
717     vkhpp::DescriptorPoolCreateInfo descriptorPoolCreateInfo = {
718         .maxSets = 10,
719         .poolSizeCount = static_cast<uint32_t>(sizes.size()),
720         .pPoolSizes = sizes.data(),
721     };
722     auto descriptorPool = device->createDescriptorPoolUnique(descriptorPoolCreateInfo).value;
723     ASSERT_THAT(descriptorPool, IsValidHandle());
724 
725     const std::vector<vkhpp::DescriptorSetLayout> descriptorSetLayouts(
726         1, *pipelineInfo->descriptorSetLayout);
727 
728     vkhpp::DescriptorSetAllocateInfo descriptorSetAllocateInfo = {
729         .descriptorPool = *descriptorPool,
730         .descriptorSetCount = 1,
731         .pSetLayouts = descriptorSetLayouts.data(),
732     };
733     auto descriptorSets = device->allocateDescriptorSetsUnique(descriptorSetAllocateInfo);
734     EXPECT_THAT(descriptorSets.result, Eq(vkhpp::Result::eSuccess));
735     auto descriptorSet = *descriptorSets.value[0];
736 
737     // A uniform for red color
738     float kColor1[] = {1.0f, 0.0f, 0.0f, 0.0f};
739     auto uniformBufferInfo = createAndPopulateBuffer(physicalDevice, device.get(),
740                                                      vkhpp::BufferUsageFlagBits::eUniformBuffer,
741                                                      kColor1, sizeof(kColor1));
742 
743     std::vector<vkhpp::WriteDescriptorSet> writeDescriptorSets;
744     std::vector<vkhpp::DescriptorBufferInfo> bufferInfos;
745     bufferInfos.emplace_back(vkhpp::DescriptorBufferInfo{
746         .buffer = *uniformBufferInfo->buffer,
747         .offset = 0,
748         .range = VK_WHOLE_SIZE,
749     });
750     writeDescriptorSets.emplace_back(vkhpp::WriteDescriptorSet{
751         .dstSet = descriptorSet,
752         .dstBinding = 0,
753         .descriptorCount = 1,
754         .descriptorType = vkhpp::DescriptorType::eUniformBuffer,
755         .pBufferInfo = bufferInfos.data(),
756     });
757     device->updateDescriptorSets(writeDescriptorSets, nullptr);
758 
759     const std::vector<vkhpp::ImageView> attachments(1, *colorAttachmentInfo->imageView);
760     vkhpp::FramebufferCreateInfo framebufferCreateInfo = {
761         .renderPass = *pipelineInfo->renderPass,
762         .attachmentCount = 1,
763         .pAttachments = attachments.data(),
764         .width = kFbWidth,
765         .height = kFbHeight,
766         .layers = 1,
767     };
768     auto framebuffer = device->createFramebufferUnique(framebufferCreateInfo).value;
769     ASSERT_THAT(framebuffer, IsValidHandle());
770 
771     auto fence = device->createFenceUnique(vkhpp::FenceCreateInfo()).value;
772     ASSERT_THAT(fence, IsValidHandle());
773 
774     const vkhpp::CommandPoolCreateInfo commandPoolCreateInfo = {
775         .flags = vkhpp::CommandPoolCreateFlagBits::eResetCommandBuffer,
776         .queueFamilyIndex = queueFamilyIndex,
777     };
778 
779     auto commandPool = device->createCommandPoolUnique(commandPoolCreateInfo).value;
780     ASSERT_THAT(commandPool, IsValidHandle());
781 
782     const vkhpp::CommandBufferAllocateInfo commandBufferAllocateInfo = {
783         .level = vkhpp::CommandBufferLevel::ePrimary,
784         .commandPool = *commandPool,
785         .commandBufferCount = 1,
786     };
787 
788     auto commandBuffers = device->allocateCommandBuffersUnique(commandBufferAllocateInfo).value;
789     ASSERT_THAT(commandBuffers, Not(IsEmpty()));
790     auto commandBuffer = std::move(commandBuffers[0]);
791     ASSERT_THAT(commandBuffer, IsValidHandle());
792 
793     vkhpp::ClearColorValue clearColor(std::array<float, 4>{0.0f, 0.0f, 0.0f, 1.0f});
794     vkhpp::ClearValue clearValue{
795         .color = clearColor,
796     };
797     vkhpp::RenderPassBeginInfo renderPassBeginInfo{
798         .renderPass = *pipelineInfo->renderPass,
799         .framebuffer = *framebuffer,
800         .renderArea = vkhpp::Rect2D(vkhpp::Offset2D(0, 0), vkhpp::Extent2D(kFbWidth, kFbHeight)),
801         .clearValueCount = 1,
802         .pClearValues = &clearValue,
803     };
804 
805     // Descriptor updates are cached on the guest, for testing purpose we need to submit a queue to
806     // commit descriptor updates.
807 
808     const vkhpp::CommandBufferBeginInfo commandBufferBeginInfo = {
809         .flags = vkhpp::CommandBufferUsageFlagBits::eOneTimeSubmit,
810     };
811 
812     commandBuffer->begin(commandBufferBeginInfo);
813     const vkhpp::ImageMemoryBarrier colorAttachmentBarrier{
814         .oldLayout = vkhpp::ImageLayout::eUndefined,
815         .newLayout = vkhpp::ImageLayout::eColorAttachmentOptimal,
816         .dstAccessMask = vkhpp::AccessFlagBits::eColorAttachmentRead |
817                          vkhpp::AccessFlagBits::eColorAttachmentWrite,
818         .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
819         .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
820         .image = *colorAttachmentInfo->image,
821         .subresourceRange =
822             {
823                 .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
824                 .levelCount = 1,
825                 .layerCount = 1,
826             },
827     };
828 
829     commandBuffer->pipelineBarrier(
830         vkhpp::PipelineStageFlagBits::eTopOfPipe | vkhpp::PipelineStageFlagBits::eTransfer,
831         vkhpp::PipelineStageFlagBits::eColorAttachmentOutput, vkhpp::DependencyFlags(), nullptr,
832         nullptr, colorAttachmentBarrier);
833 
834     commandBuffer->beginRenderPass(renderPassBeginInfo, vkhpp::SubpassContents::eInline);
835     commandBuffer->bindPipeline(vkhpp::PipelineBindPoint::eGraphics, *pipelineInfo->pipeline);
836     commandBuffer->bindDescriptorSets(vkhpp::PipelineBindPoint::eGraphics,
837                                       *pipelineInfo->pipelineLayout, 0, descriptorSet, nullptr);
838     commandBuffer->bindVertexBuffers(0, {*vertexBufferInfo->buffer}, {0});
839     commandBuffer->setViewport(0, vkhpp::Viewport(0.0f, 0.0f, static_cast<float>(kFbWidth),
840                                                   static_cast<float>(kFbHeight), 0.0f, 1.0f));
841     commandBuffer->setScissor(
842         0, vkhpp::Rect2D(vkhpp::Offset2D(0, 0), vkhpp::Extent2D(kFbWidth, kFbHeight)));
843     commandBuffer->draw(6, 1, 0, 0);
844     commandBuffer->endRenderPass();
845     commandBuffer->end();
846 
847     std::vector<vkhpp::CommandBuffer> commandBufferHandles;
848     commandBufferHandles.push_back(*commandBuffer);
849 
850     const vkhpp::SubmitInfo submitInfo = {
851         .commandBufferCount = static_cast<uint32_t>(commandBufferHandles.size()),
852         .pCommandBuffers = commandBufferHandles.data(),
853     };
854     queue.submit(submitInfo, *fence);
855     auto waitResult = device->waitForFences(*fence, VK_TRUE, 3000000000L);
856     ASSERT_THAT(waitResult, IsVkSuccess());
857     commandBuffer->reset();
858 
859     // Clear the rendering
860     commandBuffer->begin(commandBufferBeginInfo);
861     commandBuffer->beginRenderPass(renderPassBeginInfo, vkhpp::SubpassContents::eInline);
862     commandBuffer->bindPipeline(vkhpp::PipelineBindPoint::eGraphics, *pipelineInfo->pipeline);
863     vkhpp::ClearAttachment clearAttachment{
864         .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
865         .colorAttachment = 0,
866         .clearValue = clearValue,
867     };
868     vkhpp::ClearRect clearRect{
869         .rect = vkhpp::Rect2D(vkhpp::Offset2D(0, 0), vkhpp::Extent2D(kFbWidth, kFbHeight)),
870         .baseArrayLayer = 0,
871         .layerCount = 1,
872     };
873     commandBuffer->clearAttachments(1, &clearAttachment, 1, &clearRect);
874     commandBuffer->endRenderPass();
875     commandBuffer->end();
876 
877     device->resetFences(1, &fence.get());
878     queue.submit(submitInfo, *fence);
879     waitResult = device->waitForFences(*fence, VK_TRUE, 3000000000L);
880     ASSERT_THAT(waitResult, IsVkSuccess());
881     commandBuffer->reset();
882 
883     SnapshotSaveAndLoad();
884 
885     // Redraw after snapshot, verify descriptors keep their value
886     // Command buffer snapshot is not implemented yet, so we need to re-make the command buffer.
887     commandBuffer->begin(commandBufferBeginInfo);
888     commandBuffer->beginRenderPass(renderPassBeginInfo, vkhpp::SubpassContents::eInline);
889     commandBuffer->bindPipeline(vkhpp::PipelineBindPoint::eGraphics, *pipelineInfo->pipeline);
890     commandBuffer->bindDescriptorSets(vkhpp::PipelineBindPoint::eGraphics,
891                                       *pipelineInfo->pipelineLayout, 0, descriptorSet, nullptr);
892     commandBuffer->bindVertexBuffers(0, {*vertexBufferInfo->buffer}, {0});
893     commandBuffer->setViewport(0, vkhpp::Viewport(0.0f, 0.0f, static_cast<float>(kFbWidth),
894                                                   static_cast<float>(kFbHeight), 0.0f, 1.0f));
895     commandBuffer->setScissor(
896         0, vkhpp::Rect2D(vkhpp::Offset2D(0, 0), vkhpp::Extent2D(kFbWidth, kFbHeight)));
897     commandBuffer->draw(6, 1, 0, 0);
898     commandBuffer->endRenderPass();
899     commandBuffer->end();
900 
901     device->resetFences(1, &fence.get());
902     queue.submit(submitInfo, *fence);
903     waitResult = device->waitForFences(*fence, VK_TRUE, 3000000000L);
904     ASSERT_THAT(waitResult, IsVkSuccess());
905 
906     std::vector<uint32_t> dst(kFbWidth * kFbHeight);
907     utils::readImageData(*colorAttachmentInfo->image, kFbWidth, kFbHeight,
908                          vkhpp::ImageLayout::eColorAttachmentOptimal, dst.data(),
909                          dst.size() * sizeof(uint32_t), testEnvironment);
910     for (int i = 0; i < dst.size(); i++) {
911         // The shader adds a blue color (from vertex buffer) with a red color (from uniform) and get
912         // purple.
913         ASSERT_THAT(dst[i], Eq(0xffff00ff));
914     }
915 }
916 
TEST_P(GfxstreamEnd2EndVkSnapshotPipelineTest,DeleteBufferBeforeCommit)917 TEST_P(GfxstreamEnd2EndVkSnapshotPipelineTest, DeleteBufferBeforeCommit) {
918     TypicalVkTestEnvironment testEnvironment = VK_ASSERT(SetUpTypicalVkTestEnvironment());
919     auto& [instance, physicalDevice, device, queue, queueFamilyIndex] = testEnvironment;
920 
921     auto pipelineInfo = createPipeline(device.get());
922     auto vertexBufferInfo = createAndPopulateBuffer(
923         physicalDevice, device.get(), vkhpp::BufferUsageFlagBits::eVertexBuffer,
924         kFullscreenBlueRectangleVertexData, sizeof(kFullscreenBlueRectangleVertexData));
925 
926     auto colorAttachmentInfo = createColorAttachment(physicalDevice, device.get());
927     ASSERT_THAT(colorAttachmentInfo->image, IsValidHandle());
928     ASSERT_THAT(colorAttachmentInfo->memory, IsValidHandle());
929     ASSERT_THAT(colorAttachmentInfo->imageView, IsValidHandle());
930 
931     // Descriptor
932     std::vector<vkhpp::DescriptorPoolSize> sizes = {
933         {
934             .type = vkhpp::DescriptorType::eUniformBuffer,
935             .descriptorCount = 10,
936         },
937     };
938     vkhpp::DescriptorPoolCreateInfo descriptorPoolCreateInfo = {
939         .maxSets = 10,
940         .poolSizeCount = static_cast<uint32_t>(sizes.size()),
941         .pPoolSizes = sizes.data(),
942     };
943     auto descriptorPool = device->createDescriptorPoolUnique(descriptorPoolCreateInfo).value;
944     ASSERT_THAT(descriptorPool, IsValidHandle());
945 
946     const std::vector<vkhpp::DescriptorSetLayout> descriptorSetLayouts(
947         1, *pipelineInfo->descriptorSetLayout);
948 
949     vkhpp::DescriptorSetAllocateInfo descriptorSetAllocateInfo = {
950         .descriptorPool = *descriptorPool,
951         .descriptorSetCount = 1,
952         .pSetLayouts = descriptorSetLayouts.data(),
953     };
954     auto descriptorSets = device->allocateDescriptorSetsUnique(descriptorSetAllocateInfo);
955     EXPECT_THAT(descriptorSets.result, Eq(vkhpp::Result::eSuccess));
956     auto descriptorSet = *descriptorSets.value[0];
957 
958     // A uniform for red color
959     float kColor1[] = {1.0f, 0.0f, 0.0f, 0.0f};
960     auto uniformBufferInfo = createAndPopulateBuffer(physicalDevice, device.get(),
961                                                      vkhpp::BufferUsageFlagBits::eUniformBuffer,
962                                                      kColor1, sizeof(kColor1));
963 
964     std::vector<vkhpp::WriteDescriptorSet> writeDescriptorSets;
965     std::vector<vkhpp::DescriptorBufferInfo> bufferInfos;
966     bufferInfos.emplace_back(vkhpp::DescriptorBufferInfo{
967         .buffer = *uniformBufferInfo->buffer,
968         .offset = 0,
969         .range = VK_WHOLE_SIZE,
970     });
971     writeDescriptorSets.emplace_back(vkhpp::WriteDescriptorSet{
972         .dstSet = descriptorSet,
973         .dstBinding = 0,
974         .descriptorCount = 1,
975         .descriptorType = vkhpp::DescriptorType::eUniformBuffer,
976         .pBufferInfo = bufferInfos.data(),
977     });
978     device->updateDescriptorSets(writeDescriptorSets, nullptr);
979 
980     // Delete the underlying buffer, should not crash.
981     uniformBufferInfo->buffer.reset();
982 
983     const std::vector<vkhpp::ImageView> attachments(1, *colorAttachmentInfo->imageView);
984     vkhpp::FramebufferCreateInfo framebufferCreateInfo = {
985         .renderPass = *pipelineInfo->renderPass,
986         .attachmentCount = 1,
987         .pAttachments = attachments.data(),
988         .width = kFbWidth,
989         .height = kFbHeight,
990         .layers = 1,
991     };
992     auto framebuffer = device->createFramebufferUnique(framebufferCreateInfo).value;
993     ASSERT_THAT(framebuffer, IsValidHandle());
994 
995     auto fence = device->createFenceUnique(vkhpp::FenceCreateInfo()).value;
996     ASSERT_THAT(fence, IsValidHandle());
997 
998     const vkhpp::CommandPoolCreateInfo commandPoolCreateInfo = {
999         .flags = vkhpp::CommandPoolCreateFlagBits::eResetCommandBuffer,
1000         .queueFamilyIndex = queueFamilyIndex,
1001     };
1002 
1003     auto commandPool = device->createCommandPoolUnique(commandPoolCreateInfo).value;
1004     ASSERT_THAT(commandPool, IsValidHandle());
1005 
1006     const vkhpp::CommandBufferAllocateInfo commandBufferAllocateInfo = {
1007         .level = vkhpp::CommandBufferLevel::ePrimary,
1008         .commandPool = *commandPool,
1009         .commandBufferCount = 1,
1010     };
1011 
1012     auto commandBuffers = device->allocateCommandBuffersUnique(commandBufferAllocateInfo).value;
1013     ASSERT_THAT(commandBuffers, Not(IsEmpty()));
1014     auto commandBuffer = std::move(commandBuffers[0]);
1015     ASSERT_THAT(commandBuffer, IsValidHandle());
1016 
1017     vkhpp::ClearColorValue clearColor(std::array<float, 4>{0.0f, 0.0f, 0.0f, 1.0f});
1018     vkhpp::ClearValue clearValue{
1019         .color = clearColor,
1020     };
1021 
1022     // Descriptor updates are cached on the guest, for testing purpose we need to submit a queue to
1023     // commit descriptor updates.
1024 
1025     const vkhpp::CommandBufferBeginInfo commandBufferBeginInfo = {
1026         .flags = vkhpp::CommandBufferUsageFlagBits::eOneTimeSubmit,
1027     };
1028 
1029     commandBuffer->begin(commandBufferBeginInfo);
1030     const vkhpp::ImageMemoryBarrier colorAttachmentBarrier{
1031         .oldLayout = vkhpp::ImageLayout::eUndefined,
1032         .newLayout = vkhpp::ImageLayout::eColorAttachmentOptimal,
1033         .dstAccessMask = vkhpp::AccessFlagBits::eColorAttachmentRead |
1034                          vkhpp::AccessFlagBits::eColorAttachmentWrite,
1035         .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
1036         .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
1037         .image = *colorAttachmentInfo->image,
1038         .subresourceRange =
1039             {
1040                 .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
1041                 .levelCount = 1,
1042                 .layerCount = 1,
1043             },
1044     };
1045 
1046     commandBuffer->pipelineBarrier(
1047         vkhpp::PipelineStageFlagBits::eTopOfPipe | vkhpp::PipelineStageFlagBits::eTransfer,
1048         vkhpp::PipelineStageFlagBits::eColorAttachmentOutput, vkhpp::DependencyFlags(), nullptr,
1049         nullptr, colorAttachmentBarrier);
1050 
1051     vkhpp::RenderPassBeginInfo renderPassBeginInfo{
1052         .renderPass = *pipelineInfo->renderPass,
1053         .framebuffer = *framebuffer,
1054         .renderArea = vkhpp::Rect2D(vkhpp::Offset2D(0, 0), vkhpp::Extent2D(kFbWidth, kFbHeight)),
1055         .clearValueCount = 1,
1056         .pClearValues = &clearValue,
1057     };
1058     commandBuffer->beginRenderPass(renderPassBeginInfo, vkhpp::SubpassContents::eInline);
1059     vkhpp::ClearAttachment clearAttachment{
1060         .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
1061         .colorAttachment = 0,
1062         .clearValue = clearValue,
1063     };
1064     vkhpp::ClearRect clearRect{
1065         .rect = vkhpp::Rect2D(vkhpp::Offset2D(0, 0), vkhpp::Extent2D(kFbWidth, kFbHeight)),
1066         .baseArrayLayer = 0,
1067         .layerCount = 1,
1068     };
1069     commandBuffer->clearAttachments(1, &clearAttachment, 1, &clearRect);
1070     commandBuffer->endRenderPass();
1071     commandBuffer->end();
1072 
1073     std::vector<vkhpp::CommandBuffer> commandBufferHandles;
1074     commandBufferHandles.push_back(*commandBuffer);
1075 
1076     const vkhpp::SubmitInfo submitInfo = {
1077         .commandBufferCount = static_cast<uint32_t>(commandBufferHandles.size()),
1078         .pCommandBuffers = commandBufferHandles.data(),
1079     };
1080     // Submit will update the stale descriptor. Should not crash.
1081     queue.submit(submitInfo, *fence);
1082     auto waitResult = device->waitForFences(*fence, VK_TRUE, 3000000000L);
1083     ASSERT_THAT(waitResult, IsVkSuccess());
1084     commandBuffer->reset();
1085     // Snapshot should not crash.
1086     SnapshotSaveAndLoad();
1087 }
1088 
TEST_P(GfxstreamEnd2EndVkSnapshotPipelineTest,DeleteBufferAfterWriteDescriptor)1089 TEST_P(GfxstreamEnd2EndVkSnapshotPipelineTest, DeleteBufferAfterWriteDescriptor) {
1090     TypicalVkTestEnvironment testEnvironment = VK_ASSERT(SetUpTypicalVkTestEnvironment());
1091     auto& [instance, physicalDevice, device, queue, queueFamilyIndex] = testEnvironment;
1092 
1093     auto pipelineInfo = createPipeline(device.get());
1094     auto vertexBufferInfo = createAndPopulateBuffer(
1095         physicalDevice, device.get(), vkhpp::BufferUsageFlagBits::eVertexBuffer,
1096         kFullscreenBlueRectangleVertexData, sizeof(kFullscreenBlueRectangleVertexData));
1097 
1098     auto colorAttachmentInfo = createColorAttachment(physicalDevice, device.get());
1099     ASSERT_THAT(colorAttachmentInfo->image, IsValidHandle());
1100     ASSERT_THAT(colorAttachmentInfo->memory, IsValidHandle());
1101     ASSERT_THAT(colorAttachmentInfo->imageView, IsValidHandle());
1102 
1103     // Descriptor
1104     std::vector<vkhpp::DescriptorPoolSize> sizes = {
1105         {
1106             .type = vkhpp::DescriptorType::eUniformBuffer,
1107             .descriptorCount = 10,
1108         },
1109     };
1110     vkhpp::DescriptorPoolCreateInfo descriptorPoolCreateInfo = {
1111         .maxSets = 10,
1112         .poolSizeCount = static_cast<uint32_t>(sizes.size()),
1113         .pPoolSizes = sizes.data(),
1114     };
1115     auto descriptorPool = device->createDescriptorPoolUnique(descriptorPoolCreateInfo).value;
1116     ASSERT_THAT(descriptorPool, IsValidHandle());
1117 
1118     const std::vector<vkhpp::DescriptorSetLayout> descriptorSetLayouts(
1119         1, *pipelineInfo->descriptorSetLayout);
1120 
1121     vkhpp::DescriptorSetAllocateInfo descriptorSetAllocateInfo = {
1122         .descriptorPool = *descriptorPool,
1123         .descriptorSetCount = 1,
1124         .pSetLayouts = descriptorSetLayouts.data(),
1125     };
1126     auto descriptorSets = device->allocateDescriptorSetsUnique(descriptorSetAllocateInfo);
1127     EXPECT_THAT(descriptorSets.result, Eq(vkhpp::Result::eSuccess));
1128     auto descriptorSet = *descriptorSets.value[0];
1129 
1130     // A uniform for red color
1131     float kColor1[] = {1.0f, 0.0f, 0.0f, 0.0f};
1132     auto uniformBufferInfo = createAndPopulateBuffer(physicalDevice, device.get(),
1133                                                      vkhpp::BufferUsageFlagBits::eUniformBuffer,
1134                                                      kColor1, sizeof(kColor1));
1135 
1136     std::vector<vkhpp::WriteDescriptorSet> writeDescriptorSets;
1137     std::vector<vkhpp::DescriptorBufferInfo> bufferInfos;
1138     bufferInfos.emplace_back(vkhpp::DescriptorBufferInfo{
1139         .buffer = *uniformBufferInfo->buffer,
1140         .offset = 0,
1141         .range = VK_WHOLE_SIZE,
1142     });
1143     writeDescriptorSets.emplace_back(vkhpp::WriteDescriptorSet{
1144         .dstSet = descriptorSet,
1145         .dstBinding = 0,
1146         .descriptorCount = 1,
1147         .descriptorType = vkhpp::DescriptorType::eUniformBuffer,
1148         .pBufferInfo = bufferInfos.data(),
1149     });
1150     device->updateDescriptorSets(writeDescriptorSets, nullptr);
1151 
1152     const std::vector<vkhpp::ImageView> attachments(1, *colorAttachmentInfo->imageView);
1153     vkhpp::FramebufferCreateInfo framebufferCreateInfo = {
1154         .renderPass = *pipelineInfo->renderPass,
1155         .attachmentCount = 1,
1156         .pAttachments = attachments.data(),
1157         .width = kFbWidth,
1158         .height = kFbHeight,
1159         .layers = 1,
1160     };
1161     auto framebuffer = device->createFramebufferUnique(framebufferCreateInfo).value;
1162     ASSERT_THAT(framebuffer, IsValidHandle());
1163 
1164     auto fence = device->createFenceUnique(vkhpp::FenceCreateInfo()).value;
1165     ASSERT_THAT(fence, IsValidHandle());
1166 
1167     const vkhpp::CommandPoolCreateInfo commandPoolCreateInfo = {
1168         .flags = vkhpp::CommandPoolCreateFlagBits::eResetCommandBuffer,
1169         .queueFamilyIndex = queueFamilyIndex,
1170     };
1171 
1172     auto commandPool = device->createCommandPoolUnique(commandPoolCreateInfo).value;
1173     ASSERT_THAT(commandPool, IsValidHandle());
1174 
1175     const vkhpp::CommandBufferAllocateInfo commandBufferAllocateInfo = {
1176         .level = vkhpp::CommandBufferLevel::ePrimary,
1177         .commandPool = *commandPool,
1178         .commandBufferCount = 1,
1179     };
1180 
1181     auto commandBuffers = device->allocateCommandBuffersUnique(commandBufferAllocateInfo).value;
1182     ASSERT_THAT(commandBuffers, Not(IsEmpty()));
1183     auto commandBuffer = std::move(commandBuffers[0]);
1184     ASSERT_THAT(commandBuffer, IsValidHandle());
1185 
1186     vkhpp::ClearColorValue clearColor(std::array<float, 4>{0.0f, 0.0f, 0.0f, 1.0f});
1187     vkhpp::ClearValue clearValue{
1188         .color = clearColor,
1189     };
1190     vkhpp::RenderPassBeginInfo renderPassBeginInfo{
1191         .renderPass = *pipelineInfo->renderPass,
1192         .framebuffer = *framebuffer,
1193         .renderArea = vkhpp::Rect2D(vkhpp::Offset2D(0, 0), vkhpp::Extent2D(kFbWidth, kFbHeight)),
1194         .clearValueCount = 1,
1195         .pClearValues = &clearValue,
1196     };
1197 
1198     // Descriptor updates are cached on the guest, for testing purpose we need to submit a queue to
1199     // commit descriptor updates.
1200 
1201     const vkhpp::CommandBufferBeginInfo commandBufferBeginInfo = {
1202         .flags = vkhpp::CommandBufferUsageFlagBits::eOneTimeSubmit,
1203     };
1204 
1205     commandBuffer->begin(commandBufferBeginInfo);
1206     const vkhpp::ImageMemoryBarrier colorAttachmentBarrier{
1207         .oldLayout = vkhpp::ImageLayout::eUndefined,
1208         .newLayout = vkhpp::ImageLayout::eColorAttachmentOptimal,
1209         .dstAccessMask = vkhpp::AccessFlagBits::eColorAttachmentRead |
1210                          vkhpp::AccessFlagBits::eColorAttachmentWrite,
1211         .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
1212         .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
1213         .image = *colorAttachmentInfo->image,
1214         .subresourceRange =
1215             {
1216                 .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
1217                 .levelCount = 1,
1218                 .layerCount = 1,
1219             },
1220     };
1221 
1222     commandBuffer->pipelineBarrier(
1223         vkhpp::PipelineStageFlagBits::eTopOfPipe | vkhpp::PipelineStageFlagBits::eTransfer,
1224         vkhpp::PipelineStageFlagBits::eColorAttachmentOutput, vkhpp::DependencyFlags(), nullptr,
1225         nullptr, colorAttachmentBarrier);
1226 
1227     commandBuffer->beginRenderPass(renderPassBeginInfo, vkhpp::SubpassContents::eInline);
1228     commandBuffer->bindPipeline(vkhpp::PipelineBindPoint::eGraphics, *pipelineInfo->pipeline);
1229     commandBuffer->bindDescriptorSets(vkhpp::PipelineBindPoint::eGraphics,
1230                                       *pipelineInfo->pipelineLayout, 0, descriptorSet, nullptr);
1231     commandBuffer->bindVertexBuffers(0, {*vertexBufferInfo->buffer}, {0});
1232     commandBuffer->setViewport(0, vkhpp::Viewport(0.0f, 0.0f, static_cast<float>(kFbWidth),
1233                                                   static_cast<float>(kFbHeight), 0.0f, 1.0f));
1234     commandBuffer->setScissor(
1235         0, vkhpp::Rect2D(vkhpp::Offset2D(0, 0), vkhpp::Extent2D(kFbWidth, kFbHeight)));
1236     commandBuffer->draw(6, 1, 0, 0);
1237     commandBuffer->endRenderPass();
1238     commandBuffer->end();
1239 
1240     std::vector<vkhpp::CommandBuffer> commandBufferHandles;
1241     commandBufferHandles.push_back(*commandBuffer);
1242 
1243     const vkhpp::SubmitInfo submitInfo = {
1244         .commandBufferCount = static_cast<uint32_t>(commandBufferHandles.size()),
1245         .pCommandBuffers = commandBufferHandles.data(),
1246     };
1247     queue.submit(submitInfo, *fence);
1248     auto waitResult = device->waitForFences(*fence, VK_TRUE, 3000000000L);
1249     ASSERT_THAT(waitResult, IsVkSuccess());
1250     commandBuffer->reset();
1251 
1252     vertexBufferInfo->buffer.reset();
1253     // Descriptor snapshot should not crash after underlying buffer is deleted.
1254     SnapshotSaveAndLoad();
1255 }
1256 
1257 INSTANTIATE_TEST_CASE_P(GfxstreamEnd2EndTests, GfxstreamEnd2EndVkSnapshotPipelineTest,
1258                         ::testing::ValuesIn({
1259                             TestParams{
1260                                 .with_gl = false,
1261                                 .with_vk = true,
1262                                 .with_features = {"VulkanSnapshots"},
1263                             },
1264                         }),
1265                         &GetTestName);
1266 
1267 INSTANTIATE_TEST_CASE_P(GfxstreamEnd2EndTests,
1268                         GfxstreamEnd2EndVkSnapshotPipelineWithMultiSamplingTest,
1269                         ::testing::ValuesIn({
1270                             TestParams{
1271                                 .with_gl = false,
1272                                 .with_vk = true,
1273                                 .samples = 1,
1274                                 .with_features = {"VulkanSnapshots"},
1275                             },
1276                             TestParams{
1277                                 .with_gl = false,
1278                                 .with_vk = true,
1279                                 .samples = 4,
1280                                 .with_features = {"VulkanSnapshots"},
1281                             },
1282                         }),
1283                         &GetTestName);
1284 
1285 }  // namespace
1286 }  // namespace tests
1287 }  // namespace gfxstream