15void VulkanRenderer::FillFullscreenViewportScissor(VkCommandBuffer cmd, VkExtent2D ext) {
19 vp.width =
static_cast<float>(ext.width);
20 vp.height =
static_cast<float>(ext.height);
23 vkCmdSetViewport(cmd, 0, 1, &vp);
28 vkCmdSetScissor(cmd, 0, 1, &sc);
36bool VulkanRenderer::CreateHDRSceneResources() {
37 VkImageCreateInfo info{};
38 info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
39 info.imageType = VK_IMAGE_TYPE_2D;
40 info.extent.width = scExtent.width;
41 info.extent.height = scExtent.height;
42 info.extent.depth = 1;
45 info.format = m_hdrSceneFormat;
46 info.tiling = VK_IMAGE_TILING_OPTIMAL;
47 info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
50 info.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
51 | VK_IMAGE_USAGE_SAMPLED_BIT
52 | VK_IMAGE_USAGE_TRANSFER_DST_BIT
53 | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
54 info.samples = VK_SAMPLE_COUNT_1_BIT;
55 info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
56 if (vkCreateImage(device, &info,
nullptr, &m_hdrSceneImage) != VK_SUCCESS)
return false;
58 VkMemoryRequirements req;
59 vkGetImageMemoryRequirements(device, m_hdrSceneImage, &req);
60 VkMemoryAllocateInfo alloc{};
61 alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
62 alloc.allocationSize = req.size;
63 alloc.memoryTypeIndex = FindMemoryType(req.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
64 if (vkAllocateMemory(device, &alloc,
nullptr, &m_hdrSceneMemory) != VK_SUCCESS)
return false;
65 vkBindImageMemory(device, m_hdrSceneImage, m_hdrSceneMemory, 0);
67 VkImageViewCreateInfo vinfo{};
68 vinfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
69 vinfo.image = m_hdrSceneImage;
70 vinfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
71 vinfo.format = m_hdrSceneFormat;
72 vinfo.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
73 if (vkCreateImageView(device, &vinfo,
nullptr, &m_hdrSceneView) != VK_SUCCESS)
return false;
76 VkSamplerCreateInfo sinfo{};
77 sinfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
78 sinfo.magFilter = VK_FILTER_LINEAR;
79 sinfo.minFilter = VK_FILTER_LINEAR;
80 sinfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
81 sinfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
82 sinfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
83 sinfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
86 if (vkCreateSampler(device, &sinfo,
nullptr, &m_bloomSampler) != VK_SUCCESS)
return false;
92bool VulkanRenderer::CreateBloomImages() {
95 uint32_t w = std::max(1u, scExtent.width / 2);
96 uint32_t h = std::max(1u, scExtent.height / 2);
98 VkImageCreateInfo info{};
99 info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
100 info.imageType = VK_IMAGE_TYPE_2D;
101 info.extent.width = w;
102 info.extent.height = h;
103 info.extent.depth = 1;
104 info.mipLevels = BLOOM_MIP_COUNT;
105 info.arrayLayers = 1;
106 info.format = m_hdrSceneFormat;
107 info.tiling = VK_IMAGE_TILING_OPTIMAL;
108 info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
111 info.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
112 VK_IMAGE_USAGE_TRANSFER_DST_BIT;
113 info.samples = VK_SAMPLE_COUNT_1_BIT;
114 info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
115 if (vkCreateImage(device, &info,
nullptr, &m_bloomImage) != VK_SUCCESS)
return false;
117 VkMemoryRequirements req;
118 vkGetImageMemoryRequirements(device, m_bloomImage, &req);
119 VkMemoryAllocateInfo alloc{};
120 alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
121 alloc.allocationSize = req.size;
122 alloc.memoryTypeIndex = FindMemoryType(req.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
123 if (vkAllocateMemory(device, &alloc,
nullptr, &m_bloomMemory) != VK_SUCCESS)
return false;
124 vkBindImageMemory(device, m_bloomImage, m_bloomMemory, 0);
127 for (uint32_t m = 0; m < BLOOM_MIP_COUNT; ++m) {
128 m_bloomMipExtents[m].width = std::max(1u, w >> m);
129 m_bloomMipExtents[m].height = std::max(1u, h >> m);
131 VkImageViewCreateInfo vinfo{};
132 vinfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
133 vinfo.image = m_bloomImage;
134 vinfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
135 vinfo.format = m_hdrSceneFormat;
136 vinfo.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, m, 1, 0, 1};
137 if (vkCreateImageView(device, &vinfo,
nullptr, &m_bloomMipViews[m]) != VK_SUCCESS)
return false;
144bool VulkanRenderer::CreateBloomRenderPasses() {
146 auto makeRP = [&](VkAttachmentLoadOp loadOp, VkImageLayout initial,
147 VkFormat fmt, VkImageLayout finalLayout,
148 VkRenderPass& out) ->
bool {
149 VkAttachmentDescription att{};
151 att.samples = VK_SAMPLE_COUNT_1_BIT;
153 att.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
154 att.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
155 att.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
156 att.initialLayout = initial;
157 att.finalLayout = finalLayout;
159 VkAttachmentReference ref{};
161 ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
163 VkSubpassDescription sp{};
164 sp.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
165 sp.colorAttachmentCount = 1;
166 sp.pColorAttachments = &ref;
168 std::array<VkSubpassDependency, 2> d{};
169 d[0].srcSubpass = VK_SUBPASS_EXTERNAL;
171 d[0].srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
172 d[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
173 d[0].srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
174 d[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
175 d[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
178 d[1].dstSubpass = VK_SUBPASS_EXTERNAL;
179 d[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
180 d[1].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
181 d[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
182 d[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
183 d[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
185 VkRenderPassCreateInfo rp{};
186 rp.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
187 rp.attachmentCount = 1;
188 rp.pAttachments = &att;
191 rp.dependencyCount =
static_cast<uint32_t
>(d.size());
192 rp.pDependencies = d.data();
194 return vkCreateRenderPass(device, &rp,
nullptr, &out) == VK_SUCCESS;
198 if (!makeRP(VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_IMAGE_LAYOUT_UNDEFINED,
199 m_hdrSceneFormat, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
200 m_bloomRenderPass))
return false;
203 if (!makeRP(VK_ATTACHMENT_LOAD_OP_LOAD, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
204 m_hdrSceneFormat, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
205 m_bloomAddRenderPass))
return false;
208 if (!makeRP(VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_IMAGE_LAYOUT_UNDEFINED,
209 scImageFormat, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
210 m_bloomCompositeRenderPass))
return false;
216bool VulkanRenderer::CreateBloomFramebuffers() {
218 for (uint32_t m = 0; m < BLOOM_MIP_COUNT; ++m) {
219 VkFramebufferCreateInfo fb{};
220 fb.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
221 fb.renderPass = m_bloomRenderPass;
222 fb.attachmentCount = 1;
223 fb.pAttachments = &m_bloomMipViews[m];
224 fb.width = m_bloomMipExtents[m].width;
225 fb.height = m_bloomMipExtents[m].height;
227 if (vkCreateFramebuffer(device, &fb,
nullptr, &m_bloomMipFramebuffers[m]) != VK_SUCCESS)
return false;
231 m_bloomCompositeFramebuffers.resize(swapChainImageViews.size());
232 for (
size_t i = 0; i < swapChainImageViews.size(); ++i) {
233 VkFramebufferCreateInfo fb{};
234 fb.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
235 fb.renderPass = m_bloomCompositeRenderPass;
236 fb.attachmentCount = 1;
237 fb.pAttachments = &swapChainImageViews[i];
238 fb.width = scExtent.width;
239 fb.height = scExtent.height;
241 if (vkCreateFramebuffer(device, &fb,
nullptr, &m_bloomCompositeFramebuffers[i]) != VK_SUCCESS)
return false;
248bool VulkanRenderer::CreateBloomDescriptorResources() {
251 VkDescriptorSetLayoutBinding b{};
253 b.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
254 b.descriptorCount = 1;
255 b.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
256 VkDescriptorSetLayoutCreateInfo info{};
257 info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
258 info.bindingCount = 1;
260 if (vkCreateDescriptorSetLayout(device, &info,
nullptr, &m_bloomFilterDSL) != VK_SUCCESS)
return false;
264 std::array<VkDescriptorSetLayoutBinding, 3> binds{};
265 for (uint32_t i = 0; i < binds.size(); ++i) {
266 binds[i].binding = i;
267 binds[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
268 binds[i].descriptorCount = 1;
269 binds[i].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
271 VkDescriptorSetLayoutCreateInfo info{};
272 info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
273 info.bindingCount =
static_cast<uint32_t
>(binds.size());
274 info.pBindings = binds.data();
275 if (vkCreateDescriptorSetLayout(device, &info,
nullptr, &m_bloomCompositeDSL) != VK_SUCCESS)
return false;
280 VkDescriptorPoolSize sz{};
281 sz.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
282 sz.descriptorCount = (BLOOM_TRANSITION_COUNT + 3) * MAX_FRAMES_IN_FLIGHT;
284 VkDescriptorPoolCreateInfo pool{};
285 pool.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
286 pool.poolSizeCount = 1;
287 pool.pPoolSizes = &sz;
288 pool.maxSets = (BLOOM_TRANSITION_COUNT + 1) * MAX_FRAMES_IN_FLIGHT;
289 if (vkCreateDescriptorPool(device, &pool,
nullptr, &m_bloomDescriptorPool) != VK_SUCCESS)
return false;
292 for (uint32_t f = 0; f < MAX_FRAMES_IN_FLIGHT; ++f) {
293 std::array<VkDescriptorSetLayout, BLOOM_TRANSITION_COUNT> layouts;
294 layouts.fill(m_bloomFilterDSL);
295 VkDescriptorSetAllocateInfo a{};
296 a.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
297 a.descriptorPool = m_bloomDescriptorPool;
298 a.descriptorSetCount = BLOOM_TRANSITION_COUNT;
299 a.pSetLayouts = layouts.data();
300 if (vkAllocateDescriptorSets(device, &a, m_bloomFilterSets[f].data()) != VK_SUCCESS)
return false;
304 std::array<VkDescriptorSetLayout, MAX_FRAMES_IN_FLIGHT> layouts;
305 layouts.fill(m_bloomCompositeDSL);
306 VkDescriptorSetAllocateInfo a{};
307 a.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
308 a.descriptorPool = m_bloomDescriptorPool;
309 a.descriptorSetCount = MAX_FRAMES_IN_FLIGHT;
310 a.pSetLayouts = layouts.data();
311 if (vkAllocateDescriptorSets(device, &a, m_bloomCompositeSets.data()) != VK_SUCCESS)
return false;
317bool VulkanRenderer::CreateBloomPipelines() {
319 VkPushConstantRange filterPushRange{};
320 filterPushRange.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
321 filterPushRange.offset = 0;
322 filterPushRange.size = 16;
324 VkPipelineLayoutCreateInfo fliInfo{};
325 fliInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
326 fliInfo.setLayoutCount = 1;
327 fliInfo.pSetLayouts = &m_bloomFilterDSL;
328 fliInfo.pushConstantRangeCount = 1;
329 fliInfo.pPushConstantRanges = &filterPushRange;
330 if (vkCreatePipelineLayout(device, &fliInfo,
nullptr, &m_bloomFilterPipelineLayout) != VK_SUCCESS)
return false;
332 VkPushConstantRange compPushRange{};
333 compPushRange.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
334 compPushRange.offset = 0;
335 compPushRange.size = 16;
337 VkPipelineLayoutCreateInfo cliInfo{};
338 cliInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
339 cliInfo.setLayoutCount = 1;
340 cliInfo.pSetLayouts = &m_bloomCompositeDSL;
341 cliInfo.pushConstantRangeCount = 1;
342 cliInfo.pPushConstantRanges = &compPushRange;
343 if (vkCreatePipelineLayout(device, &cliInfo,
nullptr, &m_bloomCompositePipelineLayout) != VK_SUCCESS)
return false;
346 m_bloomThresholdShader =
new VulkanShader(device);
347 if (!m_bloomThresholdShader->compile(
"assets/shaders/bloom.vert.spv",
348 "assets/shaders/bloom_threshold.frag.spv"))
return false;
349 m_bloomDownsampleShader =
new VulkanShader(device);
350 if (!m_bloomDownsampleShader->compile(
"assets/shaders/bloom.vert.spv",
351 "assets/shaders/bloom_downsample.frag.spv"))
return false;
352 m_bloomUpsampleShader =
new VulkanShader(device);
353 if (!m_bloomUpsampleShader->compile(
"assets/shaders/bloom.vert.spv",
354 "assets/shaders/bloom_upsample.frag.spv"))
return false;
355 m_bloomCompositeShader =
new VulkanShader(device);
356 if (!m_bloomCompositeShader->compile(
"assets/shaders/bloom.vert.spv",
357 "assets/shaders/bloom_composite.frag.spv"))
return false;
360 VkPipelineVertexInputStateCreateInfo vin{};
361 vin.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
363 VkPipelineInputAssemblyStateCreateInfo ia{};
364 ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
365 ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
367 std::vector<VkDynamicState> dynStates = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
368 VkPipelineDynamicStateCreateInfo ds{};
369 ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
370 ds.dynamicStateCount =
static_cast<uint32_t
>(dynStates.size());
371 ds.pDynamicStates = dynStates.data();
373 VkPipelineViewportStateCreateInfo vps{};
374 vps.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
375 vps.viewportCount = 1;
376 vps.scissorCount = 1;
378 VkPipelineRasterizationStateCreateInfo rs{};
379 rs.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
380 rs.polygonMode = VK_POLYGON_MODE_FILL;
381 rs.cullMode = VK_CULL_MODE_NONE;
384 VkPipelineMultisampleStateCreateInfo ms{};
385 ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
386 ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
388 VkPipelineDepthStencilStateCreateInfo dss{};
389 dss.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
392 VkPipelineColorBlendAttachmentState blendOpaque{};
393 blendOpaque.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
394 VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
395 VkPipelineColorBlendStateCreateInfo cbOpaque{};
396 cbOpaque.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
397 cbOpaque.attachmentCount = 1;
398 cbOpaque.pAttachments = &blendOpaque;
401 VkPipelineColorBlendAttachmentState blendAdd{};
402 blendAdd.blendEnable = VK_TRUE;
403 blendAdd.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
404 blendAdd.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
405 blendAdd.colorBlendOp = VK_BLEND_OP_ADD;
406 blendAdd.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
407 blendAdd.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
408 blendAdd.alphaBlendOp = VK_BLEND_OP_ADD;
409 blendAdd.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
410 VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
411 VkPipelineColorBlendStateCreateInfo cbAdd{};
412 cbAdd.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
413 cbAdd.attachmentCount = 1;
414 cbAdd.pAttachments = &blendAdd;
417 VkPipelineShaderStageCreateInfo threshStages[] = {
418 m_bloomThresholdShader->GetVertexInfo(),
419 m_bloomThresholdShader->GetFragInfo()
421 VkGraphicsPipelineCreateInfo gpi{};
422 gpi.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
424 gpi.pStages = threshStages;
425 gpi.pVertexInputState = &vin;
426 gpi.pInputAssemblyState = &ia;
427 gpi.pViewportState = &vps;
428 gpi.pRasterizationState = &rs;
429 gpi.pMultisampleState = &ms;
430 gpi.pDepthStencilState = &dss;
431 gpi.pColorBlendState = &cbOpaque;
432 gpi.pDynamicState = &ds;
433 gpi.layout = m_bloomFilterPipelineLayout;
434 gpi.renderPass = m_bloomRenderPass;
436 if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &gpi,
nullptr, &m_bloomThresholdPipeline) != VK_SUCCESS)
return false;
439 VkPipelineShaderStageCreateInfo downStages[] = {
440 m_bloomDownsampleShader->GetVertexInfo(),
441 m_bloomDownsampleShader->GetFragInfo()
443 gpi.pStages = downStages;
444 if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &gpi,
nullptr, &m_bloomDownsamplePipeline) != VK_SUCCESS)
return false;
447 VkPipelineShaderStageCreateInfo upStages[] = {
448 m_bloomUpsampleShader->GetVertexInfo(),
449 m_bloomUpsampleShader->GetFragInfo()
451 gpi.pStages = upStages;
452 gpi.pColorBlendState = &cbAdd;
453 gpi.renderPass = m_bloomAddRenderPass;
454 if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &gpi,
nullptr, &m_bloomUpsamplePipeline) != VK_SUCCESS)
return false;
457 VkPipelineShaderStageCreateInfo compStages[] = {
458 m_bloomCompositeShader->GetVertexInfo(),
459 m_bloomCompositeShader->GetFragInfo()
461 gpi.pStages = compStages;
462 gpi.pColorBlendState = &cbOpaque;
463 gpi.layout = m_bloomCompositePipelineLayout;
464 gpi.renderPass = m_bloomCompositeRenderPass;
465 if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &gpi,
nullptr, &m_bloomCompositePipeline) != VK_SUCCESS)
return false;
471bool VulkanRenderer::CreateBloomResources() {
472 if (m_bloomResourcesCreated)
return true;
474 if (!CreateHDRSceneResources()) {
SLEAK_ERROR(
"Bloom: HDR scene resources failed");
return false; }
475 if (!CreateBloomImages()) {
SLEAK_ERROR(
"Bloom: images failed");
return false; }
476 if (!CreateBloomRenderPasses()) {
SLEAK_ERROR(
"Bloom: render passes failed");
return false; }
477 if (!CreateBloomFramebuffers()) {
SLEAK_ERROR(
"Bloom: framebuffers failed");
return false; }
478 if (!CreateBloomDescriptorResources()) {
SLEAK_ERROR(
"Bloom: descriptor resources failed");
return false; }
479 if (!CreateBloomPipelines()) {
SLEAK_ERROR(
"Bloom: pipelines failed");
return false; }
480 if (!CreateTAAResources()) {
SLEAK_ERROR(
"Bloom: TAA resources failed");
return false; }
482 m_bloomResourcesCreated =
true;
483 SLEAK_INFO(
"Bloom + HDR scene resources created ({}x{}, {} mips)",
484 scExtent.width, scExtent.height, BLOOM_MIP_COUNT);
489void VulkanRenderer::CleanupBloomResources() {
490 if (!m_bloomResourcesCreated)
return;
492 if (m_bloomThresholdPipeline) { vkDestroyPipeline(device, m_bloomThresholdPipeline,
nullptr); m_bloomThresholdPipeline = VK_NULL_HANDLE; }
493 if (m_bloomDownsamplePipeline) { vkDestroyPipeline(device, m_bloomDownsamplePipeline,
nullptr); m_bloomDownsamplePipeline = VK_NULL_HANDLE; }
494 if (m_bloomUpsamplePipeline) { vkDestroyPipeline(device, m_bloomUpsamplePipeline,
nullptr); m_bloomUpsamplePipeline = VK_NULL_HANDLE; }
495 if (m_bloomCompositePipeline) { vkDestroyPipeline(device, m_bloomCompositePipeline,
nullptr); m_bloomCompositePipeline = VK_NULL_HANDLE; }
497 if (m_bloomFilterPipelineLayout) { vkDestroyPipelineLayout(device, m_bloomFilterPipelineLayout,
nullptr); m_bloomFilterPipelineLayout = VK_NULL_HANDLE; }
498 if (m_bloomCompositePipelineLayout) { vkDestroyPipelineLayout(device, m_bloomCompositePipelineLayout,
nullptr); m_bloomCompositePipelineLayout = VK_NULL_HANDLE; }
500 delete m_bloomThresholdShader; m_bloomThresholdShader =
nullptr;
501 delete m_bloomDownsampleShader; m_bloomDownsampleShader =
nullptr;
502 delete m_bloomUpsampleShader; m_bloomUpsampleShader =
nullptr;
503 delete m_bloomCompositeShader; m_bloomCompositeShader =
nullptr;
505 if (m_bloomDescriptorPool) { vkDestroyDescriptorPool(device, m_bloomDescriptorPool,
nullptr); m_bloomDescriptorPool = VK_NULL_HANDLE; }
506 if (m_bloomFilterDSL) { vkDestroyDescriptorSetLayout(device, m_bloomFilterDSL,
nullptr); m_bloomFilterDSL = VK_NULL_HANDLE; }
507 if (m_bloomCompositeDSL) { vkDestroyDescriptorSetLayout(device, m_bloomCompositeDSL,
nullptr); m_bloomCompositeDSL = VK_NULL_HANDLE; }
509 for (
auto& fb : m_bloomCompositeFramebuffers) {
510 if (fb) vkDestroyFramebuffer(device, fb,
nullptr);
512 m_bloomCompositeFramebuffers.clear();
514 for (uint32_t m = 0; m < BLOOM_MIP_COUNT; ++m) {
515 if (m_bloomMipFramebuffers[m]) { vkDestroyFramebuffer(device, m_bloomMipFramebuffers[m],
nullptr); m_bloomMipFramebuffers[m] = VK_NULL_HANDLE; }
516 if (m_bloomMipViews[m]) { vkDestroyImageView(device, m_bloomMipViews[m],
nullptr); m_bloomMipViews[m] = VK_NULL_HANDLE; }
519 if (m_bloomRenderPass) { vkDestroyRenderPass(device, m_bloomRenderPass,
nullptr); m_bloomRenderPass = VK_NULL_HANDLE; }
520 if (m_bloomAddRenderPass) { vkDestroyRenderPass(device, m_bloomAddRenderPass,
nullptr); m_bloomAddRenderPass = VK_NULL_HANDLE; }
521 if (m_bloomCompositeRenderPass) { vkDestroyRenderPass(device, m_bloomCompositeRenderPass,
nullptr); m_bloomCompositeRenderPass = VK_NULL_HANDLE; }
523 if (m_bloomImage) { vkDestroyImage(device, m_bloomImage,
nullptr); m_bloomImage = VK_NULL_HANDLE; }
524 if (m_bloomMemory) { vkFreeMemory(device, m_bloomMemory,
nullptr); m_bloomMemory = VK_NULL_HANDLE; }
526 if (m_hdrSceneView) { vkDestroyImageView(device, m_hdrSceneView,
nullptr); m_hdrSceneView = VK_NULL_HANDLE; }
527 if (m_hdrSceneImage) { vkDestroyImage(device, m_hdrSceneImage,
nullptr); m_hdrSceneImage = VK_NULL_HANDLE; }
528 if (m_hdrSceneMemory) { vkFreeMemory(device, m_hdrSceneMemory,
nullptr); m_hdrSceneMemory = VK_NULL_HANDLE; }
530 if (m_bloomSampler) { vkDestroySampler(device, m_bloomSampler,
nullptr); m_bloomSampler = VK_NULL_HANDLE; }
532 CleanupTAAResources();
533 m_bloomResourcesCreated =
false;
538 VkImageView view, VkSampler sampler,
539 VkImageLayout layout) {
540 VkDescriptorImageInfo ii{};
541 ii.imageLayout = layout;
543 ii.sampler = sampler;
545 VkWriteDescriptorSet w{};
546 w.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
549 w.dstArrayElement = 0;
550 w.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
551 w.descriptorCount = 1;
553 vkUpdateDescriptorSets(dev, 1, &w, 0,
nullptr);
557void VulkanRenderer::RenderBloomPass() {
558 if (!m_bloomResourcesCreated)
return;
569 if (m_bloomFallbackPrimed)
return;
570 VkImageMemoryBarrier toClear{};
571 toClear.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
572 toClear.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
573 toClear.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
574 toClear.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
575 toClear.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
576 toClear.srcAccessMask = 0;
577 toClear.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
578 toClear.image = m_bloomImage;
579 toClear.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
580 vkCmdPipelineBarrier(command,
581 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
582 0, 0,
nullptr, 0,
nullptr, 1, &toClear);
584 VkClearColorValue black{};
585 VkImageSubresourceRange range = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
586 vkCmdClearColorImage(command, m_bloomImage,
587 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &black, 1, &range);
589 VkImageMemoryBarrier toRead = toClear;
590 toRead.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
591 toRead.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
592 toRead.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
593 toRead.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
594 vkCmdPipelineBarrier(command,
595 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
596 0, 0,
nullptr, 0,
nullptr, 1, &toRead);
597 m_bloomFallbackPrimed =
true;
604 m_bloomFallbackPrimed =
false;
611 auto& setArray = m_bloomFilterSets[currentFrame];
617 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
620 VkRenderPassBeginInfo rp{};
621 rp.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
622 rp.renderPass = m_bloomRenderPass;
623 rp.framebuffer = m_bloomMipFramebuffers[0];
624 rp.renderArea.offset = {0, 0};
625 rp.renderArea.extent = m_bloomMipExtents[0];
626 rp.clearValueCount = 0;
627 vkCmdBeginRenderPass(command, &rp, VK_SUBPASS_CONTENTS_INLINE);
628 FillFullscreenViewportScissor(command, m_bloomMipExtents[0]);
629 vkCmdBindPipeline(command, VK_PIPELINE_BIND_POINT_GRAPHICS, m_bloomThresholdPipeline);
630 vkCmdBindDescriptorSets(command, VK_PIPELINE_BIND_POINT_GRAPHICS,
631 m_bloomFilterPipelineLayout, 0, 1, &setArray[0], 0,
nullptr);
632 struct {
float threshold, knee, p0, p1; } thresh{ 1.0f, 0.5f, 0.0f, 0.0f };
633 vkCmdPushConstants(command, m_bloomFilterPipelineLayout,
634 VK_SHADER_STAGE_FRAGMENT_BIT, 0,
sizeof(thresh), &thresh);
635 vkCmdDraw(command, 3, 1, 0, 0);
636 vkCmdEndRenderPass(command);
640 for (uint32_t m = 1; m < BLOOM_MIP_COUNT; ++m) {
643 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
645 VkRenderPassBeginInfo rp{};
646 rp.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
647 rp.renderPass = m_bloomRenderPass;
648 rp.framebuffer = m_bloomMipFramebuffers[m];
649 rp.renderArea.offset = {0, 0};
650 rp.renderArea.extent = m_bloomMipExtents[m];
651 rp.clearValueCount = 0;
652 vkCmdBeginRenderPass(command, &rp, VK_SUBPASS_CONTENTS_INLINE);
653 FillFullscreenViewportScissor(command, m_bloomMipExtents[m]);
654 vkCmdBindPipeline(command, VK_PIPELINE_BIND_POINT_GRAPHICS, m_bloomDownsamplePipeline);
655 vkCmdBindDescriptorSets(command, VK_PIPELINE_BIND_POINT_GRAPHICS,
656 m_bloomFilterPipelineLayout, 0, 1, &setArray[setIdx], 0,
nullptr);
658 struct {
float tsx, tsy, karis, p; } pc;
659 pc.tsx = 1.0f / float(m_bloomMipExtents[m - 1].
width);
660 pc.tsy = 1.0f / float(m_bloomMipExtents[m - 1].
height);
661 pc.karis = (m == 1) ? 1.0f : 0.0f;
663 vkCmdPushConstants(command, m_bloomFilterPipelineLayout,
664 VK_SHADER_STAGE_FRAGMENT_BIT, 0,
sizeof(pc), &pc);
665 vkCmdDraw(command, 3, 1, 0, 0);
666 vkCmdEndRenderPass(command);
670 for (uint32_t m = BLOOM_MIP_COUNT - 1; m > 0; --m) {
671 uint32_t setIdx = BLOOM_MIP_COUNT + (BLOOM_MIP_COUNT - 1 - m);
673 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
675 VkRenderPassBeginInfo rp{};
676 rp.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
677 rp.renderPass = m_bloomAddRenderPass;
678 rp.framebuffer = m_bloomMipFramebuffers[m - 1];
679 rp.renderArea.offset = {0, 0};
680 rp.renderArea.extent = m_bloomMipExtents[m - 1];
681 rp.clearValueCount = 0;
682 vkCmdBeginRenderPass(command, &rp, VK_SUBPASS_CONTENTS_INLINE);
683 FillFullscreenViewportScissor(command, m_bloomMipExtents[m - 1]);
684 vkCmdBindPipeline(command, VK_PIPELINE_BIND_POINT_GRAPHICS, m_bloomUpsamplePipeline);
685 vkCmdBindDescriptorSets(command, VK_PIPELINE_BIND_POINT_GRAPHICS,
686 m_bloomFilterPipelineLayout, 0, 1, &setArray[setIdx], 0,
nullptr);
688 struct {
float tsx, tsy, radius, intensity; } pc;
689 pc.tsx = 1.0f / float(m_bloomMipExtents[m].
width);
690 pc.tsy = 1.0f / float(m_bloomMipExtents[m].
height);
693 vkCmdPushConstants(command, m_bloomFilterPipelineLayout,
694 VK_SHADER_STAGE_FRAGMENT_BIT, 0,
sizeof(pc), &pc);
695 vkCmdDraw(command, 3, 1, 0, 0);
696 vkCmdEndRenderPass(command);
701void VulkanRenderer::RenderBloomCompositePass() {
702 if (!m_bloomResourcesCreated)
return;
708 VkDescriptorSet compSet = m_bloomCompositeSets[currentFrame];
709 std::array<VkDescriptorImageInfo, 3> infos{};
710 infos[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
711 infos[0].imageView = m_hdrSceneView;
712 infos[0].sampler = m_bloomSampler;
714 infos[1].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
715 infos[1].imageView = m_bloomMipViews[0];
716 infos[1].sampler = m_bloomSampler;
722 infos[2].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
723 if (m_ssrResourcesCreated && m_ssrView != VK_NULL_HANDLE) {
724 infos[2].imageView = m_ssrView;
725 infos[2].sampler = m_ssrSampler ? m_ssrSampler : m_bloomSampler;
727 infos[2].imageView = m_bloomMipViews[0];
728 infos[2].sampler = m_bloomSampler;
731 std::array<VkWriteDescriptorSet, 3> writes{};
732 for (uint32_t i = 0; i < writes.size(); ++i) {
733 writes[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
734 writes[i].dstSet = compSet;
735 writes[i].dstBinding = i;
736 writes[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
737 writes[i].descriptorCount = 1;
738 writes[i].pImageInfo = &infos[i];
740 vkUpdateDescriptorSets(device,
static_cast<uint32_t
>(writes.size()),
741 writes.data(), 0,
nullptr);
743 VkRenderPassBeginInfo rp{};
744 rp.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
745 rp.renderPass = m_bloomCompositeRenderPass;
746 rp.framebuffer = m_bloomCompositeFramebuffers[CurrentFrameIndex];
747 rp.renderArea.offset = {0, 0};
748 rp.renderArea.extent = scExtent;
749 rp.clearValueCount = 0;
751 vkCmdBeginRenderPass(command, &rp, VK_SUBPASS_CONTENTS_INLINE);
752 FillFullscreenViewportScissor(command, scExtent);
753 vkCmdBindPipeline(command, VK_PIPELINE_BIND_POINT_GRAPHICS, m_bloomCompositePipeline);
754 vkCmdBindDescriptorSets(command, VK_PIPELINE_BIND_POINT_GRAPHICS,
755 m_bloomCompositePipelineLayout, 0, 1, &compSet, 0,
nullptr);
757 struct {
float bloomStrength, exposure, p0, p1; } pc;
763 vkCmdPushConstants(command, m_bloomCompositePipelineLayout,
764 VK_SHADER_STAGE_FRAGMENT_BIT, 0,
sizeof(pc), &pc);
765 vkCmdDraw(command, 3, 1, 0, 0);
770 ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), command);
773 vkCmdEndRenderPass(command);
Backend-facing rendering layer shared by the four graphics backends.
static void WriteSingleImageSampler(VkDevice dev, VkDescriptorSet set, VkImageView view, VkSampler sampler, VkImageLayout layout)
Writes a filter-DSL descriptor set for a given source view.
Root namespace for everything the engine exposes.