1 #include "CompositorVk.h"
2
3 #include <string.h>
4
5 #include <cinttypes>
6 #include <glm/gtc/matrix_transform.hpp>
7 #include <optional>
8
9 #include "host-common/logging.h"
10 #include "vulkan/vk_enum_string_helper.h"
11 #include "vulkan/vk_util.h"
12
13 namespace gfxstream {
14 namespace vk {
15
16 using emugl::ABORT_REASON_OTHER;
17 using emugl::FatalError;
18
19 namespace CompositorVkShader {
20 #include "vulkan/CompositorFragmentShader.h"
21 #include "vulkan/CompositorVertexShader.h"
22 } // namespace CompositorVkShader
23
24 namespace {
25
26 constexpr const VkImageLayout kSourceImageInitialLayoutUsed =
27 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
28 constexpr const VkImageLayout kSourceImageFinalLayoutUsed =
29 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
30
31 constexpr const VkImageLayout kTargetImageInitialLayoutUsed = VK_IMAGE_LAYOUT_UNDEFINED;
32 constexpr const VkImageLayout kTargetImageFinalLayoutUsed = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
33
getInfoOrAbort(const std::unique_ptr<BorrowedImageInfo> & info)34 const BorrowedImageInfoVk* getInfoOrAbort(const std::unique_ptr<BorrowedImageInfo>& info) {
35 auto imageVk = static_cast<const BorrowedImageInfoVk*>(info.get());
36 if (imageVk != nullptr) {
37 return imageVk;
38 }
39
40 GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
41 << "CompositorVk did not find BorrowedImageInfoVk";
42 }
43
44 struct Vertex {
45 alignas(8) glm::vec2 pos;
46 alignas(8) glm::vec2 tex;
47
getBindingDescriptiongfxstream::vk::__anon37a55d760111::Vertex48 static VkVertexInputBindingDescription getBindingDescription() {
49 return VkVertexInputBindingDescription{
50 .binding = 0,
51 .stride = sizeof(struct Vertex),
52 .inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
53 };
54 }
55
getAttributeDescriptiongfxstream::vk::__anon37a55d760111::Vertex56 static std::array<VkVertexInputAttributeDescription, 2> getAttributeDescription() {
57 return {
58 VkVertexInputAttributeDescription{
59 .location = 0,
60 .binding = 0,
61 .format = VK_FORMAT_R32G32_SFLOAT,
62 .offset = offsetof(struct Vertex, pos),
63 },
64 VkVertexInputAttributeDescription{
65 .location = 1,
66 .binding = 0,
67 .format = VK_FORMAT_R32G32_SFLOAT,
68 .offset = offsetof(struct Vertex, tex),
69 },
70 };
71 }
72 };
73
74 static const std::vector<Vertex> k_vertices = {
75 // clang-format off
76 { .pos = {-1.0f, -1.0f}, .tex = {0.0f, 0.0f}},
77 { .pos = { 1.0f, -1.0f}, .tex = {1.0f, 0.0f}},
78 { .pos = { 1.0f, 1.0f}, .tex = {1.0f, 1.0f}},
79 { .pos = {-1.0f, 1.0f}, .tex = {0.0f, 1.0f}},
80 // clang-format on
81 };
82
83 static const std::vector<uint16_t> k_indices = {0, 1, 2, 2, 3, 0};
84
createShaderModule(const VulkanDispatch & vk,VkDevice device,const std::vector<uint32_t> & code)85 static VkShaderModule createShaderModule(const VulkanDispatch& vk, VkDevice device,
86 const std::vector<uint32_t>& code) {
87 const VkShaderModuleCreateInfo shaderModuleCi = {
88 .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
89 .codeSize = static_cast<uint32_t>(code.size() * sizeof(uint32_t)),
90 .pCode = code.data(),
91 };
92 VkShaderModule res;
93 VK_CHECK(vk.vkCreateShaderModule(device, &shaderModuleCi, nullptr, &res));
94 return res;
95 }
96
97 } // namespace
98
RenderTarget(const VulkanDispatch & vk,VkDevice vkDevice,VkImage vkImage,VkImageView vkImageView,uint32_t width,uint32_t height,VkRenderPass vkRenderPass)99 CompositorVk::RenderTarget::RenderTarget(const VulkanDispatch& vk, VkDevice vkDevice,
100 VkImage vkImage, VkImageView vkImageView, uint32_t width,
101 uint32_t height, VkRenderPass vkRenderPass)
102 : m_vk(vk),
103 m_vkDevice(vkDevice),
104 m_vkImage(vkImage),
105 m_vkFramebuffer(VK_NULL_HANDLE),
106 m_width(width),
107 m_height(height) {
108 if (vkImageView == VK_NULL_HANDLE) {
109 GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
110 << "CompositorVk found empty image view handle when creating RenderTarget.";
111 }
112
113 const VkFramebufferCreateInfo framebufferCi = {
114 .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
115 .flags = 0,
116 .renderPass = vkRenderPass,
117 .attachmentCount = 1,
118 .pAttachments = &vkImageView,
119 .width = width,
120 .height = height,
121 .layers = 1,
122 };
123 VK_CHECK(m_vk.vkCreateFramebuffer(vkDevice, &framebufferCi, nullptr, &m_vkFramebuffer));
124 }
125
~RenderTarget()126 CompositorVk::RenderTarget::~RenderTarget() {
127 if (m_vkFramebuffer != VK_NULL_HANDLE) {
128 m_vk.vkDestroyFramebuffer(m_vkDevice, m_vkFramebuffer, nullptr);
129 }
130 }
131
create(const VulkanDispatch & vk,VkDevice vkDevice,VkPhysicalDevice vkPhysicalDevice,VkQueue vkQueue,std::shared_ptr<android::base::Lock> queueLock,uint32_t queueFamilyIndex,uint32_t maxFramesInFlight)132 std::unique_ptr<CompositorVk> CompositorVk::create(const VulkanDispatch& vk, VkDevice vkDevice,
133 VkPhysicalDevice vkPhysicalDevice,
134 VkQueue vkQueue,
135 std::shared_ptr<android::base::Lock> queueLock,
136 uint32_t queueFamilyIndex,
137 uint32_t maxFramesInFlight) {
138 auto res = std::unique_ptr<CompositorVk>(new CompositorVk(
139 vk, vkDevice, vkPhysicalDevice, vkQueue, queueLock, queueFamilyIndex, maxFramesInFlight));
140 res->setUpCommandPool();
141 res->setUpSampler();
142 res->setUpGraphicsPipeline();
143 res->setUpVertexBuffers();
144 res->setUpUniformBuffers();
145 res->setUpDescriptorSets();
146 res->setUpFences();
147 res->setUpDefaultImage();
148 res->setUpFrameResourceFutures();
149 return res;
150 }
151
CompositorVk(const VulkanDispatch & vk,VkDevice vkDevice,VkPhysicalDevice vkPhysicalDevice,VkQueue vkQueue,std::shared_ptr<android::base::Lock> queueLock,uint32_t queueFamilyIndex,uint32_t maxFramesInFlight)152 CompositorVk::CompositorVk(const VulkanDispatch& vk, VkDevice vkDevice,
153 VkPhysicalDevice vkPhysicalDevice, VkQueue vkQueue,
154 std::shared_ptr<android::base::Lock> queueLock,
155 uint32_t queueFamilyIndex, uint32_t maxFramesInFlight)
156 : CompositorVkBase(vk, vkDevice, vkPhysicalDevice, vkQueue, queueLock, queueFamilyIndex,
157 maxFramesInFlight),
158 m_maxFramesInFlight(maxFramesInFlight),
159 m_renderTargetCache(k_renderTargetCacheSize) {}
160
~CompositorVk()161 CompositorVk::~CompositorVk() {
162 {
163 android::base::AutoLock lock(*m_vkQueueLock);
164 VK_CHECK(vk_util::waitForVkQueueIdleWithRetry(m_vk, m_vkQueue));
165 }
166 if (m_defaultImage.m_vkImageView != VK_NULL_HANDLE) {
167 m_vk.vkDestroyImageView(m_vkDevice, m_defaultImage.m_vkImageView, nullptr);
168 }
169 if (m_defaultImage.m_vkImage != VK_NULL_HANDLE) {
170 m_vk.vkDestroyImage(m_vkDevice, m_defaultImage.m_vkImage, nullptr);
171 }
172 if (m_defaultImage.m_vkImageMemory != VK_NULL_HANDLE) {
173 m_vk.vkFreeMemory(m_vkDevice, m_defaultImage.m_vkImageMemory, nullptr);
174 }
175 m_vk.vkDestroyDescriptorPool(m_vkDevice, m_vkDescriptorPool, nullptr);
176 if (m_uniformStorage.m_vkDeviceMemory != VK_NULL_HANDLE) {
177 m_vk.vkUnmapMemory(m_vkDevice, m_uniformStorage.m_vkDeviceMemory);
178 }
179 m_vk.vkDestroyBuffer(m_vkDevice, m_uniformStorage.m_vkBuffer, nullptr);
180 m_vk.vkFreeMemory(m_vkDevice, m_uniformStorage.m_vkDeviceMemory, nullptr);
181 m_vk.vkFreeMemory(m_vkDevice, m_vertexVkDeviceMemory, nullptr);
182 m_vk.vkDestroyBuffer(m_vkDevice, m_vertexVkBuffer, nullptr);
183 m_vk.vkFreeMemory(m_vkDevice, m_indexVkDeviceMemory, nullptr);
184 m_vk.vkDestroyBuffer(m_vkDevice, m_indexVkBuffer, nullptr);
185 m_vk.vkDestroyPipeline(m_vkDevice, m_graphicsVkPipeline, nullptr);
186 m_vk.vkDestroyRenderPass(m_vkDevice, m_vkRenderPass, nullptr);
187 m_vk.vkDestroyPipelineLayout(m_vkDevice, m_vkPipelineLayout, nullptr);
188 m_vk.vkDestroySampler(m_vkDevice, m_vkSampler, nullptr);
189 m_vk.vkDestroyDescriptorSetLayout(m_vkDevice, m_vkDescriptorSetLayout, nullptr);
190 m_vk.vkDestroyCommandPool(m_vkDevice, m_vkCommandPool, nullptr);
191 for (PerFrameResources& frameResources : m_frameResources) {
192 m_vk.vkDestroyFence(m_vkDevice, frameResources.m_vkFence, nullptr);
193 }
194 }
195
setUpGraphicsPipeline()196 void CompositorVk::setUpGraphicsPipeline() {
197 const std::vector<uint32_t> vertSpvBuff(CompositorVkShader::compositorVertexShader,
198 std::end(CompositorVkShader::compositorVertexShader));
199 const std::vector<uint32_t> fragSpvBuff(CompositorVkShader::compositorFragmentShader,
200 std::end(CompositorVkShader::compositorFragmentShader));
201 const auto vertShaderMod = createShaderModule(m_vk, m_vkDevice, vertSpvBuff);
202 const auto fragShaderMod = createShaderModule(m_vk, m_vkDevice, fragSpvBuff);
203
204 const VkPipelineShaderStageCreateInfo shaderStageCis[2] = {
205 VkPipelineShaderStageCreateInfo{
206 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
207 .stage = VK_SHADER_STAGE_VERTEX_BIT,
208 .module = vertShaderMod,
209 .pName = "main",
210 },
211 VkPipelineShaderStageCreateInfo{
212 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
213 .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
214 .module = fragShaderMod,
215 .pName = "main",
216 },
217 };
218
219 const auto vertexAttributeDescription = Vertex::getAttributeDescription();
220 const auto vertexBindingDescription = Vertex::getBindingDescription();
221 const VkPipelineVertexInputStateCreateInfo vertexInputStateCi = {
222 .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
223 .vertexBindingDescriptionCount = 1,
224 .pVertexBindingDescriptions = &vertexBindingDescription,
225 .vertexAttributeDescriptionCount = static_cast<uint32_t>(vertexAttributeDescription.size()),
226 .pVertexAttributeDescriptions = vertexAttributeDescription.data(),
227 };
228 const VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCi = {
229 .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
230 .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
231 .primitiveRestartEnable = VK_FALSE,
232 };
233
234 const VkPipelineViewportStateCreateInfo viewportStateCi = {
235 .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
236 .viewportCount = 1,
237 // The viewport state is dynamic.
238 .pViewports = nullptr,
239 .scissorCount = 1,
240 // The scissor state is dynamic.
241 .pScissors = nullptr,
242 };
243
244 const VkPipelineRasterizationStateCreateInfo rasterizerStateCi = {
245 .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
246 .depthClampEnable = VK_FALSE,
247 .rasterizerDiscardEnable = VK_FALSE,
248 .polygonMode = VK_POLYGON_MODE_FILL,
249 .cullMode = VK_CULL_MODE_BACK_BIT,
250 .frontFace = VK_FRONT_FACE_CLOCKWISE,
251 .depthBiasEnable = VK_FALSE,
252 .depthBiasConstantFactor = 0.0f,
253 .depthBiasClamp = 0.0f,
254 .depthBiasSlopeFactor = 0.0f,
255 .lineWidth = 1.0f,
256 };
257
258 const VkPipelineMultisampleStateCreateInfo multisampleStateCi = {
259 .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
260 .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
261 .sampleShadingEnable = VK_FALSE,
262 .minSampleShading = 1.0f,
263 .pSampleMask = nullptr,
264 .alphaToCoverageEnable = VK_FALSE,
265 .alphaToOneEnable = VK_FALSE,
266 };
267
268 const VkPipelineColorBlendAttachmentState colorBlendAttachment = {
269 .blendEnable = VK_TRUE,
270 .srcColorBlendFactor = VK_BLEND_FACTOR_ONE,
271 .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
272 .colorBlendOp = VK_BLEND_OP_ADD,
273 .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
274 .dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
275 .alphaBlendOp = VK_BLEND_OP_ADD,
276 .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
277 VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
278 };
279
280 const VkPipelineColorBlendStateCreateInfo colorBlendStateCi = {
281 .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
282 .logicOpEnable = VK_FALSE,
283 .attachmentCount = 1,
284 .pAttachments = &colorBlendAttachment,
285 };
286
287 const VkDynamicState dynamicStates[] = {
288 VK_DYNAMIC_STATE_VIEWPORT,
289 VK_DYNAMIC_STATE_SCISSOR,
290 };
291 const VkPipelineDynamicStateCreateInfo dynamicStateCi = {
292 .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
293 .dynamicStateCount = std::size(dynamicStates),
294 .pDynamicStates = dynamicStates,
295 };
296
297 const VkDescriptorSetLayoutBinding layoutBindings[2] = {
298 VkDescriptorSetLayoutBinding{
299 .binding = 0,
300 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
301 .descriptorCount = 1,
302 .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
303 .pImmutableSamplers = &m_vkSampler,
304 },
305 VkDescriptorSetLayoutBinding{
306 .binding = 1,
307 .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
308 .descriptorCount = 1,
309 .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
310 .pImmutableSamplers = nullptr,
311 },
312 };
313
314 const VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCi = {
315 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
316 .pNext = nullptr,
317 .flags = 0,
318 .bindingCount = static_cast<uint32_t>(std::size(layoutBindings)),
319 .pBindings = layoutBindings,
320 };
321 VK_CHECK(m_vk.vkCreateDescriptorSetLayout(m_vkDevice, &descriptorSetLayoutCi, nullptr,
322 &m_vkDescriptorSetLayout));
323
324 const VkPipelineLayoutCreateInfo pipelineLayoutCi = {
325 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
326 .setLayoutCount = 1,
327 .pSetLayouts = &m_vkDescriptorSetLayout,
328 .pushConstantRangeCount = 0,
329 };
330
331 VK_CHECK(
332 m_vk.vkCreatePipelineLayout(m_vkDevice, &pipelineLayoutCi, nullptr, &m_vkPipelineLayout));
333
334 const VkAttachmentDescription colorAttachment = {
335 .format = VK_FORMAT_R8G8B8A8_UNORM,
336 .samples = VK_SAMPLE_COUNT_1_BIT,
337 .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
338 .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
339 .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
340 .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
341 .initialLayout = kTargetImageInitialLayoutUsed,
342 .finalLayout = kTargetImageFinalLayoutUsed,
343 };
344
345 const VkAttachmentReference colorAttachmentRef = {
346 .attachment = 0,
347 .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
348 };
349
350 const VkSubpassDescription subpass = {
351 .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
352 .colorAttachmentCount = 1,
353 .pColorAttachments = &colorAttachmentRef,
354 };
355
356 // TODO: to support multiple layer composition, we could run the same render
357 // pass for multiple time. In that case, we should use explicit
358 // VkImageMemoryBarriers to transform the image layout instead of relying on
359 // renderpass to do it.
360 const VkSubpassDependency subpassDependency = {
361 .srcSubpass = VK_SUBPASS_EXTERNAL,
362 .dstSubpass = 0,
363 .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
364 .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
365 .srcAccessMask = 0,
366 .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
367 };
368
369 const VkRenderPassCreateInfo renderPassCi = {
370 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
371 .attachmentCount = 1,
372 .pAttachments = &colorAttachment,
373 .subpassCount = 1,
374 .pSubpasses = &subpass,
375 .dependencyCount = 1,
376 .pDependencies = &subpassDependency,
377 };
378 VK_CHECK(m_vk.vkCreateRenderPass(m_vkDevice, &renderPassCi, nullptr, &m_vkRenderPass));
379
380 const VkGraphicsPipelineCreateInfo graphicsPipelineCi = {
381 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
382 .stageCount = static_cast<uint32_t>(std::size(shaderStageCis)),
383 .pStages = shaderStageCis,
384 .pVertexInputState = &vertexInputStateCi,
385 .pInputAssemblyState = &inputAssemblyStateCi,
386 .pViewportState = &viewportStateCi,
387 .pRasterizationState = &rasterizerStateCi,
388 .pMultisampleState = &multisampleStateCi,
389 .pDepthStencilState = nullptr,
390 .pColorBlendState = &colorBlendStateCi,
391 .pDynamicState = &dynamicStateCi,
392 .layout = m_vkPipelineLayout,
393 .renderPass = m_vkRenderPass,
394 .subpass = 0,
395 .basePipelineHandle = VK_NULL_HANDLE,
396 .basePipelineIndex = -1,
397 };
398 VK_CHECK(m_vk.vkCreateGraphicsPipelines(m_vkDevice, VK_NULL_HANDLE, 1, &graphicsPipelineCi,
399 nullptr, &m_graphicsVkPipeline));
400
401 m_vk.vkDestroyShaderModule(m_vkDevice, vertShaderMod, nullptr);
402 m_vk.vkDestroyShaderModule(m_vkDevice, fragShaderMod, nullptr);
403 }
404
setUpVertexBuffers()405 void CompositorVk::setUpVertexBuffers() {
406 const VkDeviceSize vertexBufferSize = sizeof(Vertex) * k_vertices.size();
407 std::tie(m_vertexVkBuffer, m_vertexVkDeviceMemory) =
408 createBuffer(vertexBufferSize,
409 VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
410 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
411 .value();
412 auto [vertexStagingBuffer, vertexStagingBufferMemory] =
413 createStagingBufferWithData(k_vertices.data(), vertexBufferSize);
414 copyBuffer(vertexStagingBuffer, m_vertexVkBuffer, vertexBufferSize);
415 m_vk.vkDestroyBuffer(m_vkDevice, vertexStagingBuffer, nullptr);
416 m_vk.vkFreeMemory(m_vkDevice, vertexStagingBufferMemory, nullptr);
417
418 VkDeviceSize indexBufferSize = sizeof(k_indices[0]) * k_indices.size();
419 auto [indexStagingBuffer, indexStagingBufferMemory] =
420 createStagingBufferWithData(k_indices.data(), indexBufferSize);
421 std::tie(m_indexVkBuffer, m_indexVkDeviceMemory) =
422 createBuffer(indexBufferSize,
423 VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
424 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
425 .value();
426
427 copyBuffer(indexStagingBuffer, m_indexVkBuffer, indexBufferSize);
428 m_vk.vkDestroyBuffer(m_vkDevice, indexStagingBuffer, nullptr);
429 m_vk.vkFreeMemory(m_vkDevice, indexStagingBufferMemory, nullptr);
430 }
431
setUpDescriptorSets()432 void CompositorVk::setUpDescriptorSets() {
433 const uint32_t descriptorSetsPerFrame = kMaxLayersPerFrame;
434 const uint32_t descriptorSetsTotal = descriptorSetsPerFrame * m_maxFramesInFlight;
435
436 const uint32_t descriptorsOfEachTypePerSet = 1;
437 const uint32_t descriptorsOfEachTypePerFrame =
438 descriptorSetsPerFrame * descriptorsOfEachTypePerSet;
439 const uint32_t descriptorsOfEachTypeTotal = descriptorsOfEachTypePerFrame * m_maxFramesInFlight;
440
441 const VkDescriptorPoolSize descriptorPoolSizes[2] = {
442 VkDescriptorPoolSize{
443 .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
444 .descriptorCount = descriptorsOfEachTypeTotal,
445 },
446 VkDescriptorPoolSize{
447 .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
448 .descriptorCount = descriptorsOfEachTypeTotal,
449 }};
450 const VkDescriptorPoolCreateInfo descriptorPoolCi = {
451 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
452 .flags = 0,
453 .maxSets = descriptorSetsTotal,
454 .poolSizeCount = static_cast<uint32_t>(std::size(descriptorPoolSizes)),
455 .pPoolSizes = descriptorPoolSizes,
456 };
457 VK_CHECK(
458 m_vk.vkCreateDescriptorPool(m_vkDevice, &descriptorPoolCi, nullptr, &m_vkDescriptorPool));
459
460 const std::vector<VkDescriptorSetLayout> frameDescriptorSetLayouts(descriptorSetsPerFrame,
461 m_vkDescriptorSetLayout);
462 const VkDescriptorSetAllocateInfo frameDescriptorSetAllocInfo = {
463 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
464 .descriptorPool = m_vkDescriptorPool,
465 .descriptorSetCount = descriptorSetsPerFrame,
466 .pSetLayouts = frameDescriptorSetLayouts.data(),
467 };
468
469 VkDeviceSize uniformBufferOffset = 0;
470 for (uint32_t frameIndex = 0; frameIndex < m_maxFramesInFlight; ++frameIndex) {
471 PerFrameResources& frameResources = m_frameResources[frameIndex];
472 frameResources.m_layerDescriptorSets.resize(descriptorSetsPerFrame);
473
474 VK_CHECK(m_vk.vkAllocateDescriptorSets(m_vkDevice, &frameDescriptorSetAllocInfo,
475 frameResources.m_layerDescriptorSets.data()));
476
477 for (uint32_t layerIndex = 0; layerIndex < kMaxLayersPerFrame; ++layerIndex) {
478 const VkDescriptorBufferInfo bufferInfo = {
479 .buffer = m_uniformStorage.m_vkBuffer,
480 .offset = uniformBufferOffset,
481 .range = sizeof(UniformBufferBinding),
482 };
483 const VkWriteDescriptorSet descriptorSetWrite = {
484 .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
485 .dstSet = frameResources.m_layerDescriptorSets[layerIndex],
486 .dstBinding = 1,
487 .dstArrayElement = 0,
488 .descriptorCount = 1,
489 .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
490 .pBufferInfo = &bufferInfo,
491 };
492 m_vk.vkUpdateDescriptorSets(m_vkDevice, 1, &descriptorSetWrite, 0, nullptr);
493
494 uniformBufferOffset += m_uniformStorage.m_stride;
495 }
496 }
497 }
498
setUpCommandPool()499 void CompositorVk::setUpCommandPool() {
500 const VkCommandPoolCreateInfo commandPoolCreateInfo = {
501 .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
502 .flags = 0,
503 .queueFamilyIndex = m_queueFamilyIndex,
504 };
505
506 VkCommandPool commandPool = VK_NULL_HANDLE;
507 VK_CHECK(m_vk.vkCreateCommandPool(m_vkDevice, &commandPoolCreateInfo, nullptr, &commandPool));
508 m_vkCommandPool = commandPool;
509 }
510
setUpFences()511 void CompositorVk::setUpFences() {
512 for (uint32_t frameIndex = 0; frameIndex < m_maxFramesInFlight; ++frameIndex) {
513 PerFrameResources& frameResources = m_frameResources[frameIndex];
514
515 const VkFenceCreateInfo fenceCi = {
516 .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
517 };
518
519 VkFence fence;
520 VK_CHECK(m_vk.vkCreateFence(m_vkDevice, &fenceCi, nullptr, &fence));
521
522 frameResources.m_vkFence = fence;
523 }
524 }
525
setUpDefaultImage()526 void CompositorVk::setUpDefaultImage() {
527 const VkImageCreateInfo imageCreateInfo = {
528 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
529 .pNext = nullptr,
530 .flags = 0,
531 .imageType = VK_IMAGE_TYPE_2D,
532 .format = VK_FORMAT_R8G8B8A8_UNORM,
533 .extent =
534 {
535 .width = 2,
536 .height = 2,
537 .depth = 1,
538 },
539 .mipLevels = 1,
540 .arrayLayers = 1,
541 .samples = VK_SAMPLE_COUNT_1_BIT,
542 .tiling = VK_IMAGE_TILING_OPTIMAL,
543 .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
544 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
545 .queueFamilyIndexCount = 0,
546 .pQueueFamilyIndices = nullptr,
547 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
548 };
549 VkImage image = VK_NULL_HANDLE;
550 VK_CHECK(m_vk.vkCreateImage(m_vkDevice, &imageCreateInfo, nullptr, &image));
551
552 VkMemoryRequirements imageMemoryRequirements;
553 m_vk.vkGetImageMemoryRequirements(m_vkDevice, image, &imageMemoryRequirements);
554
555 auto memoryTypeIndexOpt =
556 findMemoryType(imageMemoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
557 if (!memoryTypeIndexOpt) {
558 GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
559 << "CompositorVk failed to find memory type for default image.";
560 }
561
562 const VkMemoryAllocateInfo imageMemoryAllocInfo = {
563 .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
564 .pNext = nullptr,
565 .allocationSize = imageMemoryRequirements.size,
566 .memoryTypeIndex = *memoryTypeIndexOpt,
567 };
568 VkDeviceMemory imageMemory = VK_NULL_HANDLE;
569 VK_CHECK_MEMALLOC(
570 m_vk.vkAllocateMemory(m_vkDevice, &imageMemoryAllocInfo, nullptr, &imageMemory),
571 imageMemoryAllocInfo);
572
573 VK_CHECK(m_vk.vkBindImageMemory(m_vkDevice, image, imageMemory, 0));
574
575 const VkImageViewCreateInfo imageViewCreateInfo = {
576 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
577 .pNext = nullptr,
578 .flags = 0,
579 .image = image,
580 .viewType = VK_IMAGE_VIEW_TYPE_2D,
581 .format = VK_FORMAT_R8G8B8A8_UNORM,
582 .components =
583 {
584 .r = VK_COMPONENT_SWIZZLE_IDENTITY,
585 .g = VK_COMPONENT_SWIZZLE_IDENTITY,
586 .b = VK_COMPONENT_SWIZZLE_IDENTITY,
587 .a = VK_COMPONENT_SWIZZLE_IDENTITY,
588 },
589 .subresourceRange =
590 {
591 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
592 .baseMipLevel = 0,
593 .levelCount = 1,
594 .baseArrayLayer = 0,
595 .layerCount = 1,
596 },
597 };
598 VkImageView imageView = VK_NULL_HANDLE;
599 VK_CHECK(m_vk.vkCreateImageView(m_vkDevice, &imageViewCreateInfo, nullptr, &imageView));
600
601 const std::vector<uint8_t> pixels = {
602 0xFF, 0x00, 0xFF, 0xFF, //
603 0xFF, 0x00, 0xFF, 0xFF, //
604 0xFF, 0x00, 0xFF, 0xFF, //
605 0xFF, 0x00, 0xFF, 0xFF, //
606 };
607 VkBuffer stagingBuffer = VK_NULL_HANDLE;
608 VkDeviceMemory stagingBufferMemory = VK_NULL_HANDLE;
609 std::tie(stagingBuffer, stagingBufferMemory) =
610 createStagingBufferWithData(pixels.data(), pixels.size());
611
612 runSingleTimeCommands(m_vkQueue, m_vkQueueLock, [&, this](const VkCommandBuffer& cmdBuff) {
613 const VkImageMemoryBarrier toTransferDstImageBarrier = {
614 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
615 .pNext = nullptr,
616 .srcAccessMask = 0,
617 .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
618 .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
619 .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
620 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
621 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
622 .image = image,
623 .subresourceRange =
624 {
625 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
626 .baseMipLevel = 0,
627 .levelCount = 1,
628 .baseArrayLayer = 0,
629 .layerCount = 1,
630 },
631 };
632 m_vk.vkCmdPipelineBarrier(cmdBuff,
633 /*srcStageMask=*/VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
634 /*dstStageMask=*/VK_PIPELINE_STAGE_TRANSFER_BIT,
635 /*dependencyFlags=*/0,
636 /*memoryBarrierCount=*/0,
637 /*pMemoryBarriers=*/nullptr,
638 /*bufferMemoryBarrierCount=*/0,
639 /*pBufferMemoryBarriers=*/nullptr, 1, &toTransferDstImageBarrier);
640
641 const VkBufferImageCopy bufferToImageCopy = {
642 .bufferOffset = 0,
643 .bufferRowLength = 0,
644 .bufferImageHeight = 0,
645 .imageSubresource =
646 {
647 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
648 .mipLevel = 0,
649 .baseArrayLayer = 0,
650 .layerCount = 1,
651 },
652 .imageOffset =
653 {
654 .x = 0,
655 .y = 0,
656 .z = 0,
657 },
658 .imageExtent =
659 {
660 .width = 2,
661 .height = 2,
662 .depth = 1,
663 },
664 };
665 m_vk.vkCmdCopyBufferToImage(cmdBuff, stagingBuffer, image,
666 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &bufferToImageCopy);
667
668 const VkImageMemoryBarrier toSampledImageImageBarrier = {
669 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
670 .pNext = nullptr,
671 .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
672 .dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
673 .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
674 .newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
675 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
676 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
677 .image = image,
678 .subresourceRange =
679 {
680 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
681 .baseMipLevel = 0,
682 .levelCount = 1,
683 .baseArrayLayer = 0,
684 .layerCount = 1,
685 },
686 };
687 m_vk.vkCmdPipelineBarrier(cmdBuff,
688 /*srcStageMask=*/VK_PIPELINE_STAGE_TRANSFER_BIT,
689 /*dstStageMask=*/VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
690 /*dependencyFlags=*/0,
691 /*memoryBarrierCount=*/0,
692 /*pMemoryBarriers=*/nullptr,
693 /*bufferMemoryBarrierCount=*/0,
694 /*pBufferMemoryBarriers=*/nullptr, 1,
695 &toSampledImageImageBarrier);
696 });
697
698 m_vk.vkDestroyBuffer(m_vkDevice, stagingBuffer, nullptr);
699 m_vk.vkFreeMemory(m_vkDevice, stagingBufferMemory, nullptr);
700
701 m_defaultImage.m_vkImage = image;
702 m_defaultImage.m_vkImageView = imageView;
703 m_defaultImage.m_vkImageMemory = imageMemory;
704 }
705
setUpFrameResourceFutures()706 void CompositorVk::setUpFrameResourceFutures() {
707 for (uint32_t frameIndex = 0; frameIndex < m_maxFramesInFlight; ++frameIndex) {
708 std::shared_future<PerFrameResources*> availableFrameResourceFuture =
709 std::async(std::launch::deferred, [this, frameIndex] {
710 return &m_frameResources[frameIndex];
711 }).share();
712
713 m_availableFrameResources.push_back(std::move(availableFrameResourceFuture));
714 }
715 }
716
setUpUniformBuffers()717 void CompositorVk::setUpUniformBuffers() {
718 VkPhysicalDeviceProperties physicalDeviceProperties;
719 m_vk.vkGetPhysicalDeviceProperties(m_vkPhysicalDevice, &physicalDeviceProperties);
720 const VkDeviceSize alignment = physicalDeviceProperties.limits.minUniformBufferOffsetAlignment;
721 m_uniformStorage.m_stride = ((sizeof(UniformBufferBinding) - 1) / alignment + 1) * alignment;
722
723 VkDeviceSize size = m_uniformStorage.m_stride * m_maxFramesInFlight * kMaxLayersPerFrame;
724 auto maybeBuffer =
725 createBuffer(size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
726 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
727 VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
728 auto buffer = std::make_tuple<VkBuffer, VkDeviceMemory>(VK_NULL_HANDLE, VK_NULL_HANDLE);
729 if (maybeBuffer.has_value()) {
730 buffer = maybeBuffer.value();
731 } else {
732 buffer =
733 createBuffer(size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
734 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)
735 .value();
736 }
737 std::tie(m_uniformStorage.m_vkBuffer, m_uniformStorage.m_vkDeviceMemory) = buffer;
738
739 void* mapped = nullptr;
740 VK_CHECK(m_vk.vkMapMemory(m_vkDevice, m_uniformStorage.m_vkDeviceMemory, 0, size, 0, &mapped));
741
742 uint8_t* data = reinterpret_cast<uint8_t*>(mapped);
743 for (uint32_t frameIndex = 0; frameIndex < m_maxFramesInFlight; ++frameIndex) {
744 PerFrameResources& frameResources = m_frameResources[frameIndex];
745 for (uint32_t layerIndex = 0; layerIndex < kMaxLayersPerFrame; ++layerIndex) {
746 auto* layerUboStorage = reinterpret_cast<UniformBufferBinding*>(data);
747 frameResources.m_layerUboStorages.push_back(layerUboStorage);
748 data += m_uniformStorage.m_stride;
749 }
750 }
751 }
752
setUpSampler()753 void CompositorVk::setUpSampler() {
754 // The texture coordinate transformation matrices for flip/rotate/etc
755 // currently depends on this being repeat.
756 constexpr const VkSamplerAddressMode kSamplerMode = VK_SAMPLER_ADDRESS_MODE_REPEAT;
757
758 const VkSamplerCreateInfo samplerCi = {
759 .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
760 .magFilter = VK_FILTER_LINEAR,
761 .minFilter = VK_FILTER_LINEAR,
762 .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
763 .addressModeU = kSamplerMode,
764 .addressModeV = kSamplerMode,
765 .addressModeW = kSamplerMode,
766 .mipLodBias = 0.0f,
767 .anisotropyEnable = VK_FALSE,
768 .maxAnisotropy = 1.0f,
769 .compareEnable = VK_FALSE,
770 .compareOp = VK_COMPARE_OP_ALWAYS,
771 .minLod = 0.0f,
772 .maxLod = 0.0f,
773 .borderColor = VK_BORDER_COLOR_INT_TRANSPARENT_BLACK,
774 .unnormalizedCoordinates = VK_FALSE,
775 };
776 VK_CHECK(m_vk.vkCreateSampler(m_vkDevice, &samplerCi, nullptr, &m_vkSampler));
777 }
778
779 // Create a VkBuffer and a bound VkDeviceMemory. When the specified memory type
780 // can't be found, return std::nullopt. When Vulkan call fails, terminate the
781 // program.
createBuffer(VkDeviceSize size,VkBufferUsageFlags usage,VkMemoryPropertyFlags memProperty) const782 std::optional<std::tuple<VkBuffer, VkDeviceMemory>> CompositorVk::createBuffer(
783 VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags memProperty) const {
784 const VkBufferCreateInfo bufferCi = {
785 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
786 .size = size,
787 .usage = usage,
788 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
789 };
790 VkBuffer resBuffer;
791 VK_CHECK(m_vk.vkCreateBuffer(m_vkDevice, &bufferCi, nullptr, &resBuffer));
792 VkMemoryRequirements memRequirements;
793 m_vk.vkGetBufferMemoryRequirements(m_vkDevice, resBuffer, &memRequirements);
794 VkPhysicalDeviceMemoryProperties physicalMemProperties;
795 m_vk.vkGetPhysicalDeviceMemoryProperties(m_vkPhysicalDevice, &physicalMemProperties);
796 auto maybeMemoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, memProperty);
797 if (!maybeMemoryTypeIndex.has_value()) {
798 ERR("Failed to find memory type for creating buffer.");
799 m_vk.vkDestroyBuffer(m_vkDevice, resBuffer, nullptr);
800 return std::nullopt;
801 }
802 const VkMemoryAllocateInfo memAllocInfo = {
803 .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
804 .allocationSize = memRequirements.size,
805 .memoryTypeIndex = maybeMemoryTypeIndex.value(),
806 };
807 VkDeviceMemory resMemory;
808 VK_CHECK_MEMALLOC(m_vk.vkAllocateMemory(m_vkDevice, &memAllocInfo, nullptr, &resMemory),
809 memAllocInfo);
810 VK_CHECK(m_vk.vkBindBufferMemory(m_vkDevice, resBuffer, resMemory, 0));
811 return std::make_tuple(resBuffer, resMemory);
812 }
813
createStagingBufferWithData(const void * srcData,VkDeviceSize size) const814 std::tuple<VkBuffer, VkDeviceMemory> CompositorVk::createStagingBufferWithData(
815 const void* srcData, VkDeviceSize size) const {
816 auto [stagingBuffer, stagingBufferMemory] =
817 createBuffer(size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
818 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)
819 .value();
820 void* data;
821 VK_CHECK(m_vk.vkMapMemory(m_vkDevice, stagingBufferMemory, 0, size, 0, &data));
822 memcpy(data, srcData, size);
823 m_vk.vkUnmapMemory(m_vkDevice, stagingBufferMemory);
824 return std::make_tuple(stagingBuffer, stagingBufferMemory);
825 }
826
copyBuffer(VkBuffer src,VkBuffer dst,VkDeviceSize size) const827 void CompositorVk::copyBuffer(VkBuffer src, VkBuffer dst, VkDeviceSize size) const {
828 runSingleTimeCommands(m_vkQueue, m_vkQueueLock, [&, this](const auto& cmdBuff) {
829 VkBufferCopy copyRegion = {};
830 copyRegion.srcOffset = 0;
831 copyRegion.dstOffset = 0;
832 copyRegion.size = size;
833 m_vk.vkCmdCopyBuffer(cmdBuff, src, dst, 1, ©Region);
834 });
835 }
836
837 // TODO: move this to another common CRTP helper class in vk_util.h.
getFormatFeatures(VkFormat format,VkImageTiling tiling)838 VkFormatFeatureFlags CompositorVk::getFormatFeatures(VkFormat format, VkImageTiling tiling) {
839 auto i = m_vkFormatProperties.find(format);
840 if (i == m_vkFormatProperties.end()) {
841 VkFormatProperties formatProperties;
842 m_vk.vkGetPhysicalDeviceFormatProperties(m_vkPhysicalDevice, format, &formatProperties);
843 i = m_vkFormatProperties.emplace(format, formatProperties).first;
844 }
845 const VkFormatProperties& formatProperties = i->second;
846 VkFormatFeatureFlags formatFeatures = 0;
847 if (tiling == VK_IMAGE_TILING_LINEAR) {
848 formatFeatures = formatProperties.linearTilingFeatures;
849 } else if (tiling == VK_IMAGE_TILING_OPTIMAL) {
850 formatFeatures = formatProperties.optimalTilingFeatures;
851 } else {
852 ERR("Unknown tiling:%#" PRIx64 ".", static_cast<uint64_t>(tiling));
853 }
854 return formatFeatures;
855 }
856
getOrCreateRenderTargetInfo(const BorrowedImageInfoVk & imageInfo)857 CompositorVk::RenderTarget* CompositorVk::getOrCreateRenderTargetInfo(
858 const BorrowedImageInfoVk& imageInfo) {
859 auto* renderTargetPtr = m_renderTargetCache.get(imageInfo.id);
860 if (renderTargetPtr != nullptr) {
861 return renderTargetPtr->get();
862 }
863
864 auto* renderTarget = new RenderTarget(m_vk, m_vkDevice, imageInfo.image, imageInfo.imageView,
865 imageInfo.imageCreateInfo.extent.width,
866 imageInfo.imageCreateInfo.extent.height, m_vkRenderPass);
867
868 m_renderTargetCache.set(imageInfo.id, std::unique_ptr<RenderTarget>(renderTarget));
869
870 return renderTarget;
871 }
872
canCompositeFrom(const VkImageCreateInfo & imageCi)873 bool CompositorVk::canCompositeFrom(const VkImageCreateInfo& imageCi) {
874 VkFormatFeatureFlags formatFeatures = getFormatFeatures(imageCi.format, imageCi.tiling);
875 if (!(formatFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) {
876 ERR("The format, %s, with tiling, %s, doesn't support the "
877 "VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT feature. All supported features are %s.",
878 string_VkFormat(imageCi.format), string_VkImageTiling(imageCi.tiling),
879 string_VkFormatFeatureFlags(formatFeatures).c_str());
880 return false;
881 }
882 return true;
883 }
884
canCompositeTo(const VkImageCreateInfo & imageCi)885 bool CompositorVk::canCompositeTo(const VkImageCreateInfo& imageCi) {
886 VkFormatFeatureFlags formatFeatures = getFormatFeatures(imageCi.format, imageCi.tiling);
887 if (!(formatFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)) {
888 ERR("The format, %s, with tiling, %s, doesn't support the "
889 "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT feature. All supported features are %s.",
890 string_VkFormat(imageCi.format), string_VkImageTiling(imageCi.tiling),
891 string_VkFormatFeatureFlags(formatFeatures).c_str());
892 return false;
893 }
894 if (!(imageCi.usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)) {
895 ERR("The VkImage is not created with the VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT usage flag. "
896 "The usage flags are %s.",
897 string_VkImageUsageFlags(imageCi.usage).c_str());
898 return false;
899 }
900 if (imageCi.format != k_renderTargetFormat) {
901 ERR("The format of the image, %s, is not supported by the CompositorVk as the render "
902 "target.",
903 string_VkFormat(imageCi.format));
904 return false;
905 }
906 return true;
907 }
908
buildCompositionVk(const CompositionRequest & compositionRequest,CompositionVk * compositionVk)909 void CompositorVk::buildCompositionVk(const CompositionRequest& compositionRequest,
910 CompositionVk* compositionVk) {
911 const BorrowedImageInfoVk* targetImage = getInfoOrAbort(compositionRequest.target);
912 RenderTarget* targetImageRenderTarget = getOrCreateRenderTargetInfo(*targetImage);
913
914 const uint32_t targetWidth = targetImage->width;
915 const uint32_t targetHeight = targetImage->height;
916
917 compositionVk->targetImage = targetImage;
918 compositionVk->targetFramebuffer = targetImageRenderTarget->m_vkFramebuffer;
919
920 for (const CompositionRequestLayer& layer : compositionRequest.layers) {
921 uint32_t sourceImageWidth = 0;
922 uint32_t sourceImageHeight = 0;
923 const BorrowedImageInfoVk* sourceImage = nullptr;
924
925 if (layer.props.composeMode == HWC2_COMPOSITION_SOLID_COLOR) {
926 sourceImageWidth = targetWidth;
927 sourceImageHeight = targetHeight;
928 } else {
929 sourceImage = getInfoOrAbort(layer.source);
930 if (!canCompositeFrom(sourceImage->imageCreateInfo)) {
931 continue;
932 }
933
934 sourceImageWidth = sourceImage->width;
935 sourceImageHeight = sourceImage->height;
936 }
937
938 // Calculate the posTransform and the texcoordTransform needed in the
939 // uniform of the Compositor.vert shader. The posTransform should transform
940 // the square(top = -1, bottom = 1, left = -1, right = 1) to the position
941 // where the layer should be drawn in NDC space given the layer.
942 // texcoordTransform should transform the unit square(top = 0, bottom = 1,
943 // left = 0, right = 1) to where we should sample the layer in the
944 // normalized uv space given the composeLayer.
945 const hwc_rect_t& posRect = layer.props.displayFrame;
946 const hwc_frect_t& texcoordRect = layer.props.crop;
947
948 const int posWidth = posRect.right - posRect.left;
949 const int posHeight = posRect.bottom - posRect.top;
950
951 const float posScaleX = float(posWidth) / targetWidth;
952 const float posScaleY = float(posHeight) / targetHeight;
953
954 const float posTranslateX = -1.0f + posScaleX + 2.0f * float(posRect.left) / targetWidth;
955 const float posTranslateY = -1.0f + posScaleY + 2.0f * float(posRect.top) / targetHeight;
956
957 float texCoordScaleX = (texcoordRect.right - texcoordRect.left) / float(sourceImageWidth);
958 float texCoordScaleY = (texcoordRect.bottom - texcoordRect.top) / float(sourceImageHeight);
959
960 const float texCoordTranslateX = texcoordRect.left / float(sourceImageWidth);
961 const float texCoordTranslateY = texcoordRect.top / float(sourceImageHeight);
962
963 float texcoordRotation = 0.0f;
964
965 const float pi = glm::pi<float>();
966
967 switch (layer.props.transform) {
968 case HWC_TRANSFORM_NONE:
969 break;
970 case HWC_TRANSFORM_ROT_90:
971 texcoordRotation = pi * 0.5f;
972 break;
973 case HWC_TRANSFORM_ROT_180:
974 texcoordRotation = pi;
975 break;
976 case HWC_TRANSFORM_ROT_270:
977 texcoordRotation = pi * 1.5f;
978 break;
979 case HWC_TRANSFORM_FLIP_H:
980 texCoordScaleX *= -1.0f;
981 break;
982 case HWC_TRANSFORM_FLIP_V:
983 texCoordScaleY *= -1.0f;
984 break;
985 case HWC_TRANSFORM_FLIP_H_ROT_90:
986 texcoordRotation = pi * 0.5f;
987 texCoordScaleX *= -1.0f;
988 break;
989 case HWC_TRANSFORM_FLIP_V_ROT_90:
990 texcoordRotation = pi * 0.5f;
991 texCoordScaleY *= -1.0f;
992 break;
993 default:
994 ERR("Unknown transform:%d", static_cast<int>(layer.props.transform));
995 break;
996 }
997
998 DescriptorSetContents descriptorSetContents = {
999 .binding1 =
1000 {
1001 .positionTransform =
1002 glm::translate(glm::mat4(1.0f),
1003 glm::vec3(posTranslateX, posTranslateY, 0.0f)) *
1004 glm::scale(glm::mat4(1.0f), glm::vec3(posScaleX, posScaleY, 1.0f)),
1005 .texCoordTransform =
1006 glm::translate(glm::mat4(1.0f),
1007 glm::vec3(texCoordTranslateX, texCoordTranslateY, 0.0f)) *
1008 glm::scale(glm::mat4(1.0f),
1009 glm::vec3(texCoordScaleX, texCoordScaleY, 1.0f)) *
1010 glm::rotate(glm::mat4(1.0f), texcoordRotation, glm::vec3(0.0f, 0.0f, 1.0f)),
1011 .mode = glm::uvec4(static_cast<uint32_t>(layer.props.composeMode), 0, 0, 0),
1012 .alpha =
1013 glm::vec4(layer.props.alpha, layer.props.alpha, layer.props.alpha,
1014 layer.props.alpha),
1015 },
1016 };
1017
1018 if (layer.props.composeMode == HWC2_COMPOSITION_SOLID_COLOR) {
1019 descriptorSetContents.binding0.sampledImageView = m_defaultImage.m_vkImageView;
1020 descriptorSetContents.binding1.color =
1021 glm::vec4(static_cast<float>(layer.props.color.r) / 255.0f,
1022 static_cast<float>(layer.props.color.g) / 255.0f,
1023 static_cast<float>(layer.props.color.b) / 255.0f,
1024 static_cast<float>(layer.props.color.a) / 255.0f);
1025 } else {
1026 if (sourceImage == nullptr) {
1027 GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
1028 << "CompositorVk failed to find sourceImage.";
1029 }
1030 descriptorSetContents.binding0.sampledImageView = sourceImage->imageView;
1031 compositionVk->layersSourceImages.emplace_back(sourceImage);
1032 }
1033
1034 compositionVk->layersDescriptorSets.descriptorSets.emplace_back(descriptorSetContents);
1035 }
1036 }
1037
compose(const CompositionRequest & compositionRequest)1038 CompositorVk::CompositionFinishedWaitable CompositorVk::compose(
1039 const CompositionRequest& compositionRequest) {
1040 CompositionVk compositionVk;
1041 buildCompositionVk(compositionRequest, &compositionVk);
1042
1043 // Grab and wait for the next available resources.
1044 if (m_availableFrameResources.empty()) {
1045 GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
1046 << "CompositorVk failed to get PerFrameResources.";
1047 }
1048 auto frameResourceFuture = std::move(m_availableFrameResources.front());
1049 m_availableFrameResources.pop_front();
1050 PerFrameResources* frameResources = frameResourceFuture.get();
1051
1052 updateDescriptorSetsIfChanged(compositionVk.layersDescriptorSets, frameResources);
1053
1054 std::vector<VkImageMemoryBarrier> preCompositionQueueTransferBarriers;
1055 std::vector<VkImageMemoryBarrier> preCompositionLayoutTransitionBarriers;
1056 std::vector<VkImageMemoryBarrier> postCompositionLayoutTransitionBarriers;
1057 std::vector<VkImageMemoryBarrier> postCompositionQueueTransferBarriers;
1058 addNeededBarriersToUseBorrowedImage(
1059 *compositionVk.targetImage, m_queueFamilyIndex, kTargetImageInitialLayoutUsed,
1060 kTargetImageFinalLayoutUsed, VK_ACCESS_MEMORY_WRITE_BIT,
1061 &preCompositionQueueTransferBarriers, &preCompositionLayoutTransitionBarriers,
1062 &postCompositionLayoutTransitionBarriers, &postCompositionQueueTransferBarriers);
1063 for (const BorrowedImageInfoVk* sourceImage : compositionVk.layersSourceImages) {
1064 addNeededBarriersToUseBorrowedImage(
1065 *sourceImage, m_queueFamilyIndex, kSourceImageInitialLayoutUsed,
1066 kSourceImageFinalLayoutUsed, VK_ACCESS_SHADER_READ_BIT,
1067 &preCompositionQueueTransferBarriers, &preCompositionLayoutTransitionBarriers,
1068 &postCompositionLayoutTransitionBarriers, &postCompositionQueueTransferBarriers);
1069 }
1070
1071 VkCommandBuffer& commandBuffer = frameResources->m_vkCommandBuffer;
1072 if (commandBuffer != VK_NULL_HANDLE) {
1073 m_vk.vkFreeCommandBuffers(m_vkDevice, m_vkCommandPool, 1, &commandBuffer);
1074 }
1075
1076 const VkCommandBufferAllocateInfo commandBufferAllocInfo = {
1077 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
1078 .commandPool = m_vkCommandPool,
1079 .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
1080 .commandBufferCount = 1,
1081 };
1082 VK_CHECK(m_vk.vkAllocateCommandBuffers(m_vkDevice, &commandBufferAllocInfo, &commandBuffer));
1083
1084 const VkCommandBufferBeginInfo beginInfo = {
1085 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
1086 .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
1087 };
1088 VK_CHECK(m_vk.vkBeginCommandBuffer(commandBuffer, &beginInfo));
1089
1090 if (!preCompositionQueueTransferBarriers.empty()) {
1091 m_vk.vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
1092 VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr,
1093 static_cast<uint32_t>(preCompositionQueueTransferBarriers.size()),
1094 preCompositionQueueTransferBarriers.data());
1095 }
1096 if (!preCompositionLayoutTransitionBarriers.empty()) {
1097 m_vk.vkCmdPipelineBarrier(
1098 commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
1099 0, 0, nullptr, 0, nullptr,
1100 static_cast<uint32_t>(preCompositionLayoutTransitionBarriers.size()),
1101 preCompositionLayoutTransitionBarriers.data());
1102 }
1103
1104 const VkClearValue renderTargetClearColor = {
1105 .color =
1106 {
1107 .float32 = {0.0f, 0.0f, 0.0f, 1.0f},
1108 },
1109 };
1110 const VkRenderPassBeginInfo renderPassBeginInfo = {
1111 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
1112 .renderPass = m_vkRenderPass,
1113 .framebuffer = compositionVk.targetFramebuffer,
1114 .renderArea =
1115 {
1116 .offset =
1117 {
1118 .x = 0,
1119 .y = 0,
1120 },
1121 .extent =
1122 {
1123 .width = compositionVk.targetImage->imageCreateInfo.extent.width,
1124 .height = compositionVk.targetImage->imageCreateInfo.extent.height,
1125 },
1126 },
1127 .clearValueCount = 1,
1128 .pClearValues = &renderTargetClearColor,
1129 };
1130 m_vk.vkCmdBeginRenderPass(commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
1131
1132 m_vk.vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_graphicsVkPipeline);
1133
1134 const VkRect2D scissor = {
1135 .offset =
1136 {
1137 .x = 0,
1138 .y = 0,
1139 },
1140 .extent =
1141 {
1142 .width = compositionVk.targetImage->imageCreateInfo.extent.width,
1143 .height = compositionVk.targetImage->imageCreateInfo.extent.height,
1144 },
1145 };
1146 m_vk.vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
1147
1148 const VkViewport viewport = {
1149 .x = 0.0f,
1150 .y = 0.0f,
1151 .width = static_cast<float>(compositionVk.targetImage->imageCreateInfo.extent.width),
1152 .height = static_cast<float>(compositionVk.targetImage->imageCreateInfo.extent.height),
1153 .minDepth = 0.0f,
1154 .maxDepth = 1.0f,
1155 };
1156 m_vk.vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
1157
1158 const VkDeviceSize offsets[] = {0};
1159 m_vk.vkCmdBindVertexBuffers(commandBuffer, 0, 1, &m_vertexVkBuffer, offsets);
1160
1161 m_vk.vkCmdBindIndexBuffer(commandBuffer, m_indexVkBuffer, 0, VK_INDEX_TYPE_UINT16);
1162
1163 const uint32_t numLayers = compositionVk.layersDescriptorSets.descriptorSets.size();
1164 for (uint32_t layerIndex = 0; layerIndex < numLayers; ++layerIndex) {
1165 VkDescriptorSet layerDescriptorSet = frameResources->m_layerDescriptorSets[layerIndex];
1166
1167 m_vk.vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
1168 m_vkPipelineLayout,
1169 /*firstSet=*/0,
1170 /*descriptorSetCount=*/1, &layerDescriptorSet,
1171 /*dynamicOffsetCount=*/0,
1172 /*pDynamicOffsets=*/nullptr);
1173
1174 m_vk.vkCmdDrawIndexed(commandBuffer, static_cast<uint32_t>(k_indices.size()), 1, 0, 0, 0);
1175 }
1176
1177 m_vk.vkCmdEndRenderPass(commandBuffer);
1178
1179 // Insert a VkImageMemoryBarrier so that the vkCmdBlitImage in post will wait for the rendering
1180 // to the render target to complete.
1181 const VkImageMemoryBarrier renderTargetBarrier = {
1182 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
1183 .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
1184 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
1185 .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
1186 .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
1187 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
1188 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
1189 .image = compositionVk.targetImage->image,
1190 .subresourceRange =
1191 {
1192 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
1193 .baseMipLevel = 0,
1194 .levelCount = 1,
1195 .baseArrayLayer = 0,
1196 .layerCount = 1,
1197 },
1198 };
1199 m_vk.vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
1200 VK_PIPELINE_STAGE_TRANSFER_BIT,
1201 /*dependencyFlags=*/0,
1202 /*memoryBarrierCount=*/0,
1203 /*pMemoryBarriers=*/nullptr,
1204 /*bufferMemoryBarrierCount=*/0,
1205 /*pBufferMemoryBarriers=*/nullptr, 1, &renderTargetBarrier);
1206
1207 if (!postCompositionLayoutTransitionBarriers.empty()) {
1208 m_vk.vkCmdPipelineBarrier(
1209 commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
1210 0, 0, nullptr, 0, nullptr,
1211 static_cast<uint32_t>(postCompositionLayoutTransitionBarriers.size()),
1212 postCompositionLayoutTransitionBarriers.data());
1213 }
1214 if (!postCompositionQueueTransferBarriers.empty()) {
1215 m_vk.vkCmdPipelineBarrier(
1216 commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
1217 0, 0, nullptr, 0, nullptr,
1218 static_cast<uint32_t>(postCompositionQueueTransferBarriers.size()),
1219 postCompositionQueueTransferBarriers.data());
1220 }
1221
1222 VK_CHECK(m_vk.vkEndCommandBuffer(commandBuffer));
1223
1224 VkFence composeCompleteFence = frameResources->m_vkFence;
1225 VK_CHECK(m_vk.vkResetFences(m_vkDevice, 1, &composeCompleteFence));
1226
1227 const VkPipelineStageFlags submitWaitStages[] = {
1228 VK_PIPELINE_STAGE_TRANSFER_BIT,
1229 };
1230 const VkSubmitInfo submitInfo = {
1231 .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
1232 .waitSemaphoreCount = 0,
1233 .pWaitSemaphores = nullptr,
1234 .pWaitDstStageMask = submitWaitStages,
1235 .commandBufferCount = 1,
1236 .pCommandBuffers = &commandBuffer,
1237 .signalSemaphoreCount = 0,
1238 .pSignalSemaphores = nullptr,
1239 };
1240
1241 {
1242 android::base::AutoLock lock(*m_vkQueueLock);
1243 VK_CHECK(m_vk.vkQueueSubmit(m_vkQueue, 1, &submitInfo, composeCompleteFence));
1244 }
1245
1246 // Create a future that will return the PerFrameResources to the next
1247 // iteration of CompostiorVk::compose() once this current composition
1248 // completes.
1249 std::shared_future<PerFrameResources*> composeCompleteFutureForResources =
1250 std::async(std::launch::deferred, [composeCompleteFence, frameResources, this]() mutable {
1251 VkResult res = m_vk.vkWaitForFences(m_vkDevice, 1, &composeCompleteFence, VK_TRUE,
1252 kVkWaitForFencesTimeoutNsecs);
1253 if (res == VK_SUCCESS) {
1254 return frameResources;
1255 }
1256 if (res == VK_TIMEOUT) {
1257 // Retry. If device lost, hopefully this returns immediately.
1258 res = m_vk.vkWaitForFences(m_vkDevice, 1, &composeCompleteFence, VK_TRUE,
1259 kVkWaitForFencesTimeoutNsecs);
1260 }
1261 VK_CHECK(res);
1262 return frameResources;
1263 }).share();
1264 m_availableFrameResources.push_back(composeCompleteFutureForResources);
1265
1266 // Create a future that will return once this current composition
1267 // completes that can be shared outside of CompositorVk.
1268 std::shared_future<void> composeCompleteFuture =
1269 std::async(std::launch::deferred, [composeCompleteFutureForResources]() {
1270 composeCompleteFutureForResources.get();
1271 }).share();
1272
1273 return composeCompleteFuture;
1274 }
1275
onImageDestroyed(uint32_t imageId)1276 void CompositorVk::onImageDestroyed(uint32_t imageId) { m_renderTargetCache.remove(imageId); }
1277
operator ==(const CompositorVkBase::DescriptorSetContents & lhs,const CompositorVkBase::DescriptorSetContents & rhs)1278 bool operator==(const CompositorVkBase::DescriptorSetContents& lhs,
1279 const CompositorVkBase::DescriptorSetContents& rhs) {
1280 return std::tie(lhs.binding0.sampledImageView, //
1281 lhs.binding1.mode, //
1282 lhs.binding1.alpha, //
1283 lhs.binding1.color, //
1284 lhs.binding1.positionTransform, //
1285 lhs.binding1.texCoordTransform) //
1286 == //
1287 std::tie(rhs.binding0.sampledImageView, //
1288 rhs.binding1.mode, //
1289 rhs.binding1.alpha, //
1290 rhs.binding1.color, //
1291 rhs.binding1.positionTransform, //
1292 rhs.binding1.texCoordTransform);
1293 }
1294
operator ==(const CompositorVkBase::FrameDescriptorSetsContents & lhs,const CompositorVkBase::FrameDescriptorSetsContents & rhs)1295 bool operator==(const CompositorVkBase::FrameDescriptorSetsContents& lhs,
1296 const CompositorVkBase::FrameDescriptorSetsContents& rhs) {
1297 return lhs.descriptorSets == rhs.descriptorSets;
1298 }
1299
updateDescriptorSetsIfChanged(const FrameDescriptorSetsContents & descriptorSetsContents,PerFrameResources * frameResources)1300 void CompositorVk::updateDescriptorSetsIfChanged(
1301 const FrameDescriptorSetsContents& descriptorSetsContents, PerFrameResources* frameResources) {
1302 if (frameResources->m_vkDescriptorSetsContents == descriptorSetsContents) {
1303 return;
1304 }
1305
1306 const uint32_t numRequestedLayers =
1307 static_cast<uint32_t>(descriptorSetsContents.descriptorSets.size());
1308 if (numRequestedLayers > kMaxLayersPerFrame) {
1309 GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
1310 << "CompositorVk can't compose more than " << kMaxLayersPerFrame
1311 << " layers. layers asked: " << numRequestedLayers;
1312 return;
1313 }
1314
1315 std::vector<VkDescriptorImageInfo> descriptorImageInfos(numRequestedLayers);
1316 std::vector<VkWriteDescriptorSet> descriptorWrites;
1317 for (uint32_t layerIndex = 0; layerIndex < numRequestedLayers; ++layerIndex) {
1318 const DescriptorSetContents& layerDescriptorSetContents =
1319 descriptorSetsContents.descriptorSets[layerIndex];
1320
1321 descriptorImageInfos[layerIndex] = VkDescriptorImageInfo{
1322 // Empty as we only use immutable samplers.
1323 .sampler = VK_NULL_HANDLE,
1324 .imageView = layerDescriptorSetContents.binding0.sampledImageView,
1325 .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
1326 };
1327
1328 descriptorWrites.emplace_back(VkWriteDescriptorSet{
1329 .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
1330 .dstSet = frameResources->m_layerDescriptorSets[layerIndex],
1331 .dstBinding = 0,
1332 .dstArrayElement = 0,
1333 .descriptorCount = 1,
1334 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
1335 .pImageInfo = &descriptorImageInfos[layerIndex],
1336 });
1337
1338 UniformBufferBinding* layerUboStorage = frameResources->m_layerUboStorages[layerIndex];
1339 *layerUboStorage = layerDescriptorSetContents.binding1;
1340 }
1341
1342 m_vk.vkUpdateDescriptorSets(m_vkDevice, descriptorWrites.size(), descriptorWrites.data(), 0,
1343 nullptr);
1344
1345 frameResources->m_vkDescriptorSetsContents = descriptorSetsContents;
1346 }
1347
1348 } // namespace vk
1349 } // namespace gfxstream
1350