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