9#include <SDL3/SDL_vulkan.h>
31#include "SDL3/SDL_error.h"
32#include "SDL3/SDL_video.h"
34 #include "vulkan/vulkan_wayland.h"
35#elif defined(PLATFORM_WIN)
36 #include <vulkan/vulkan_win32.h>
47 clearColor = {{0.3f, 0.4f, 1.0f, 1.0f}};
62 auto* tex =
new VulkanTexture(device, physicalDevice, commands, graphicsQueue);
63 tex->SetMaxMipLevels(maxMip);
64 if (tex->LoadFromMemory(data, w, h, fmt)) {
65 WriteTextureDescriptors(tex);
84 if (!SetupDebugMessenger()) {
85 SLEAK_WARN(
"Failed to setup validation layer of vulkan instance")
94 if (!CreateSwapChain())
97 if (!CreateImageViews())
100 if (!CreateDepthResources())
103 if (!CreateMSAAColorResources())
106 if (!CreateRenderPass())
109 if (!CreateDescriptorSetLayout())
112 if (!CreateDescriptorPool())
115 if (!AllocateDescriptorSets())
118 if (!CreateCommandPool())
121 if (!CreateCommandBuffer())
124 if (!CreateDefaultTexture())
125 SLEAK_WARN(
"Failed to create default white texture for Vulkan");
127 if (!CreateGraphicsPipeline())
130 if (!CreateShadowLightUBOResources())
131 SLEAK_WARN(
"Failed to create light UBO resources — dynamic lighting disabled");
133 if (!CreateShadowResources())
134 SLEAK_WARN(
"Failed to create shadow mapping resources — shadows disabled");
136 if (!CreateFrameBuffer())
141 if (!CreateGBufferResources())
142 SLEAK_WARN(
"Failed to create GBuffer resources — deferred rendering disabled");
148 if (!CreateBoneUBOResources())
149 SLEAK_WARN(
"Failed to pre-create bone UBO resources — skinned meshes may malfunction on first frame");
151 if (!CreateSyncObjects())
156 SLEAK_INFO(
"Vulkan renderer has been initialized successfully!");
165 bFrameStarted =
false;
166 m_inGeometryPass =
false;
167 m_inForwardTransparentPass =
false;
168 m_forwardPassOpen =
false;
169 m_activeCustomFormat = 0;
175 if (m_hasPendingLightVP) {
176 memcpy(m_lightVP, m_pendingLightVP,
sizeof(m_lightVP));
189 if (device && !inFlightFences.empty()) {
190 vkWaitForFences(device, 1, &inFlightFences[currentFrame],
191 VK_TRUE, UINT64_MAX);
197 auto& flush = m_asyncFlush[currentFrame];
198 if (flush.submitted) {
200 if (flush.commandBuffer != VK_NULL_HANDLE) {
201 vkFreeCommandBuffers(flush.device, flush.commandPool, 1,
202 &flush.commandBuffer);
204 for (
auto& pending : flush.stagingBuffers) {
208 pending.memoryTypeIndex);
222 result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX,
223 imageAvailableSemaphores[m_semaphoreIndex],
224 VK_NULL_HANDLE, &CurrentFrameIndex);
225 if (result == VK_ERROR_OUT_OF_DATE_KHR) {
228 }
else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
234 if (CurrentFrameIndex < imagesInFlight.size() &&
235 imagesInFlight[CurrentFrameIndex] != VK_NULL_HANDLE &&
236 imagesInFlight[CurrentFrameIndex] != inFlightFences[currentFrame]) {
237 vkWaitForFences(device, 1, &imagesInFlight[CurrentFrameIndex],
238 VK_TRUE, UINT64_MAX);
240 imagesInFlight[CurrentFrameIndex] = inFlightFences[currentFrame];
243 vkResetFences(device, 1, &inFlightFences[currentFrame]);
246 command = commandBuffers[currentFrame];
249 vkResetCommandBuffer(command, 0);
251 VkCommandBufferBeginInfo beginInfo{};
252 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
255 if (vkBeginCommandBuffer(command, &beginInfo) != VK_SUCCESS) {
260 bFrameStarted =
true;
261 m_pbrMaterialSlot[currentFrame] = 0;
265 bool hasShadowDraws = shadowQueue && shadowQueue->HasCachedShadowDraws();
268 VkClearValue shadowClear{};
269 shadowClear.depthStencil = {1.0f, 0};
271 VkRenderPassBeginInfo shadowPassInfo{};
272 shadowPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
273 shadowPassInfo.renderPass = m_shadowRenderPass;
274 shadowPassInfo.framebuffer = m_shadowFramebuffer;
275 shadowPassInfo.renderArea.offset = {0, 0};
277 shadowPassInfo.clearValueCount = 1;
278 shadowPassInfo.pClearValues = &shadowClear;
280 vkCmdBeginRenderPass(command, &shadowPassInfo, VK_SUBPASS_CONTENTS_INLINE);
281 vkCmdBindPipeline(command, VK_PIPELINE_BIND_POINT_GRAPHICS, m_shadowPipeline);
287 if (m_boneUBOCreated) {
288 vkCmdBindDescriptorSets(command, VK_PIPELINE_BIND_POINT_GRAPHICS,
290 &boneDescriptorSets[currentFrame], 0,
nullptr);
293 VkViewport shadowViewport{};
294 shadowViewport.x = 0.0f;
295 shadowViewport.y = 0.0f;
298 shadowViewport.minDepth = 0.0f;
299 shadowViewport.maxDepth = 1.0f;
300 vkCmdSetViewport(command, 0, 1, &shadowViewport);
302 VkRect2D shadowScissor{};
303 shadowScissor.offset = {0, 0};
305 vkCmdSetScissor(command, 0, 1, &shadowScissor);
307 m_shadowPassActive =
true;
308 m_shadowPCCacheValid =
false;
311 queue->ExecuteShadowPass(
this);
313 m_shadowPassActive =
false;
315 vkCmdEndRenderPass(command);
321 VkClearValue gbufferClears[GBUFFER_COUNT + 1];
322 for (uint32_t i = 0; i < GBUFFER_COUNT; ++i) {
323 gbufferClears[i].color = {0.0f, 0.0f, 0.0f, 0.0f};
325 gbufferClears[GBUFFER_COUNT].depthStencil = {1.0f, 0};
327 VkRenderPassBeginInfo gbufferPassInfo{};
328 gbufferPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
329 gbufferPassInfo.renderPass = m_gbufferRenderPass;
330 gbufferPassInfo.framebuffer = m_gbufferFramebuffer;
331 gbufferPassInfo.renderArea.offset = {0, 0};
332 gbufferPassInfo.renderArea.extent = scExtent;
333 gbufferPassInfo.clearValueCount = GBUFFER_COUNT + 1;
334 gbufferPassInfo.pClearValues = gbufferClears;
336 vkCmdBeginRenderPass(command, &gbufferPassInfo, VK_SUBPASS_CONTENTS_INLINE);
337 vkCmdBindPipeline(command, VK_PIPELINE_BIND_POINT_GRAPHICS, m_gbufferPipeline);
340 VkViewport viewport{};
343 viewport.width =
static_cast<float>(scExtent.width);
344 viewport.height =
static_cast<float>(scExtent.height);
345 viewport.minDepth = 0.0f;
346 viewport.maxDepth = 1.0f;
347 vkCmdSetViewport(command, 0, 1, &viewport);
350 scissor.offset = {0, 0};
351 scissor.extent = scExtent;
352 vkCmdSetScissor(command, 0, 1, &scissor);
359 if (m_gbufferGeomLayout != VK_NULL_HANDLE && m_lightUBOCreated) {
360 if (m_boneUBOCreated) {
361 vkCmdBindDescriptorSets(command, VK_PIPELINE_BIND_POINT_GRAPHICS,
362 m_gbufferGeomLayout, 1, 1,
363 &boneDescriptorSets[currentFrame], 0,
nullptr);
365 vkCmdBindDescriptorSets(command, VK_PIPELINE_BIND_POINT_GRAPHICS,
366 m_gbufferGeomLayout, 2, 1,
367 &m_lightUBODescriptorSets[currentFrame], 0,
nullptr);
368 vkCmdBindDescriptorSets(command, VK_PIPELINE_BIND_POINT_GRAPHICS,
369 m_gbufferGeomLayout, 3, 1,
370 &m_shadowSamplerDescriptorSets[currentFrame], 0,
nullptr);
373 m_inGeometryPass =
true;
378 ImGui_ImplVulkan_NewFrame();
379 ImGui_ImplSDL3_NewFrame();
380 auto& io = ImGui::GetIO();
381 if (io.DisplaySize.x > 0.0f && io.DisplaySize.y > 0.0f) {
392 VkClearValue clearValues[3];
393 uint32_t clearValueCount = 2;
394 clearValues[0] = clearColor;
395 clearValues[1].depthStencil = {1.0f, 0};
396 if (m_msaaSamples != VK_SAMPLE_COUNT_1_BIT) {
397 clearValues[2].color = clearColor.color;
401 VkRenderPassBeginInfo passInfo{};
402 passInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
403 passInfo.renderPass = renderPass;
404 passInfo.framebuffer = swapChainFramebuffers[CurrentFrameIndex];
405 passInfo.renderArea.offset = {0, 0};
406 passInfo.renderArea.extent = scExtent;
407 passInfo.clearValueCount = clearValueCount;
408 passInfo.pClearValues = clearValues;
410 vkCmdBeginRenderPass(command, &passInfo, VK_SUBPASS_CONTENTS_INLINE);
412 vkCmdBindPipeline(command, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
415 if (m_textureDescriptorsWritten &&
416 CurrentFrameIndex < descriptorSets.size()) {
417 vkCmdBindDescriptorSets(
418 command, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLay, 0, 1,
419 &descriptorSets[CurrentFrameIndex], 0,
nullptr);
423 VkViewport viewport{};
426 viewport.width =
static_cast<float>(scExtent.width);
427 viewport.height =
static_cast<float>(scExtent.height);
428 viewport.minDepth = 0.0f;
429 viewport.maxDepth = 1.0f;
430 vkCmdSetViewport(command, 0, 1, &viewport);
433 scissor.offset = {0, 0};
434 scissor.extent = scExtent;
435 vkCmdSetScissor(command, 0, 1, &scissor);
438 if (m_lightUBOCreated) {
439 vkCmdBindDescriptorSets(
440 command, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLay, 2, 1,
441 &m_lightUBODescriptorSets[currentFrame], 0,
nullptr);
442 vkCmdBindDescriptorSets(
443 command, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLay, 3, 1,
444 &m_shadowSamplerDescriptorSets[currentFrame], 0,
nullptr);
452 ImGui_ImplVulkan_NewFrame();
453 ImGui_ImplSDL3_NewFrame();
454 auto& io = ImGui::GetIO();
455 if (io.DisplaySize.x > 0.0f && io.DisplaySize.y > 0.0f) {
464 if (!bRender || !bFrameStarted)
467 const bool deferredPath =
473 vkCmdEndRenderPass(command);
474 m_inGeometryPass =
false;
481 if (deferredPath && !m_forwardPassOpen && !m_inGeometryPass) {
482 VkRenderPassBeginInfo rpBegin{};
483 rpBegin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
484 rpBegin.renderPass = m_forwardRenderPass;
485 rpBegin.framebuffer = m_forwardFramebuffers[CurrentFrameIndex];
486 rpBegin.renderArea.offset = {0, 0};
487 rpBegin.renderArea.extent = scExtent;
488 rpBegin.clearValueCount = 0;
489 vkCmdBeginRenderPass(command, &rpBegin, VK_SUBPASS_CONTENTS_INLINE);
490 m_forwardPassOpen =
true;
494 if (m_forwardPassOpen) {
495 vkCmdEndRenderPass(command);
496 m_forwardPassOpen =
false;
510 RenderBloomCompositePass();
515 ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), command);
517 vkCmdEndRenderPass(command);
520 if (vkEndCommandBuffer(command) != VK_SUCCESS) {
526 VkSubmitInfo submitInfo{};
527 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
530 VkSemaphore waitSemaphores[2];
531 VkPipelineStageFlags waitStages[2];
532 uint32_t waitCount = 0;
534 waitSemaphores[waitCount] = imageAvailableSemaphores[m_semaphoreIndex];
535 waitStages[waitCount] = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
538 if (m_asyncFlush[currentFrame].submitted) {
539 waitSemaphores[waitCount] = m_transferSemaphores[currentFrame];
540 waitStages[waitCount] = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
544 submitInfo.waitSemaphoreCount = waitCount;
545 submitInfo.pWaitSemaphores = waitSemaphores;
546 submitInfo.pWaitDstStageMask = waitStages;
548 submitInfo.commandBufferCount = 1;
549 submitInfo.pCommandBuffers = &command;
554 VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[CurrentFrameIndex]};
555 submitInfo.signalSemaphoreCount = 1;
556 submitInfo.pSignalSemaphores = signalSemaphores;
558 if (vkQueueSubmit(graphicsQueue, 1, &submitInfo,
559 inFlightFences[currentFrame]) != VK_SUCCESS) {
560 SLEAK_ERROR(
"Failed to submit draw command buffer!");
564 VkPresentInfoKHR presentInfo{};
565 presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
566 presentInfo.waitSemaphoreCount = 1;
567 presentInfo.pWaitSemaphores = signalSemaphores;
569 VkSwapchainKHR swapChains[] = {swapChain};
570 presentInfo.swapchainCount = 1;
571 presentInfo.pSwapchains = swapChains;
572 presentInfo.pImageIndices = &CurrentFrameIndex;
574 VkResult presentResult = vkQueuePresentKHR(presentQueue, &presentInfo);
576 if (presentResult == VK_ERROR_OUT_OF_DATE_KHR ||
577 presentResult == VK_SUBOPTIMAL_KHR) {
579 }
else if (presentResult != VK_SUCCESS) {
584 m_forwardPassOpen =
false;
585 m_inForwardTransparentPass =
false;
586 m_inGeometryPass =
false;
587 bFrameStarted =
false;
589 currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
590 m_semaphoreIndex = (m_semaphoreIndex + 1) %
591 static_cast<uint32_t
>(imageAvailableSemaphores.size());
597bool VulkanRenderer::CustomFormatDrawsSuppressed()
const {
598 return m_activeCustomFormat != 0 && m_customFormatUnbound;
605 if (!bFrameStarted)
return;
606 if (CustomFormatDrawsSuppressed())
return;
607 vkCmdDraw(command, vertexCount, 1, firstVertex, 0);
608 if (!m_shadowPassActive) {
620 int32_t baseVertex) {
621 if (!bFrameStarted)
return;
622 if (CustomFormatDrawsSuppressed())
return;
623 vkCmdDrawIndexed(command, indexCount, 1, firstIndex, baseVertex, 0);
624 if (!m_shadowPassActive) {
632 uint32_t vertexPerInstance) {
633 if (!bFrameStarted)
return;
634 if (CustomFormatDrawsSuppressed())
return;
635 vkCmdDraw(command, vertexPerInstance, instanceCount, 0, 0);
640 uint32_t indexPerInstance) {
645 uint32_t indexPerInstance,
647 int32_t baseVertex) {
648 if (!bFrameStarted)
return;
649 if (CustomFormatDrawsSuppressed())
return;
650 vkCmdDrawIndexed(command, indexPerInstance, instanceCount, firstIndex,
669 float height,
float minDepth,
671 if (!bFrameStarted)
return;
672 VkViewport viewport{};
675 viewport.width =
width;
677 viewport.minDepth = minDepth;
678 viewport.maxDepth = maxDepth;
679 vkCmdSetViewport(command, 0, 1, &viewport);
685 clearColor = {{r, g, b, a}};
690 float depth, uint8_t stencil) {
698 if (!bFrameStarted)
return;
703 if (wantFormat != 0) {
705 }
else if (m_activeCustomFormat != 0) {
709 VkBuffer buffers[] = {vkBuf->GetVkBuffer()};
710 VkDeviceSize offsets[] = {0};
711 vkCmdBindVertexBuffers(command, slot, 1, buffers, offsets);
717 if (!bFrameStarted)
return;
720 vkCmdBindIndexBuffer(command, vkBuf->GetVkBuffer(), 0,
721 VK_INDEX_TYPE_UINT32);
728 if (!bFrameStarted)
return;
736 uint32_t size =
static_cast<uint32_t
>(vkBuf->GetSize());
737 if (size > 128) size = 128;
741 VkPipelineLayout activeLayout = (m_inGeometryPass && m_gbufferGeomLayout != VK_NULL_HANDLE)
742 ? m_gbufferGeomLayout : pipelineLay;
747 if (m_inGeometryPass && !m_shadowPassActive &&
m_taaEnabled &&
748 (m_taaJitter[0] != 0.0f || m_taaJitter[1] != 0.0f) && size >= 64) {
750 memcpy(jdata, data, size);
751 const float jx = m_taaJitter[0] * 2.0f;
752 const float jy = -m_taaJitter[1] * 2.0f;
755 for (
int r = 0; r < 4; ++r) {
756 jdata[r * 4 + 0] += jx * jdata[r * 4 + 3];
757 jdata[r * 4 + 1] += jy * jdata[r * 4 + 3];
759 vkCmdPushConstants(command, activeLayout,
760 VK_SHADER_STAGE_VERTEX_BIT, 0, size, jdata);
764 if (m_shadowPassActive && slot == 0 && size >= 128) {
769 const float* srcWorld =
reinterpret_cast<const float*
>(
770 static_cast<const char*
>(data) + 64);
772 if (!m_shadowPCCacheValid ||
773 memcmp(srcWorld, m_shadowWorldCache, 64) != 0) {
775 for (
int r = 0; r < 4; ++r) {
776 for (
int c = 0; c < 4; ++c) {
778 for (
int k = 0; k < 4; ++k) {
779 sum += srcWorld[r * 4 + k] * m_lightVP[k * 4 + c];
781 m_shadowPCCache[r * 4 + c] = sum;
784 memcpy(&m_shadowPCCache[16], srcWorld, 64);
785 memcpy(m_shadowWorldCache, srcWorld, 64);
786 m_shadowPCCacheValid =
true;
789 vkCmdPushConstants(command, activeLayout,
790 VK_SHADER_STAGE_VERTEX_BIT, 0, 128, m_shadowPCCache);
792 vkCmdPushConstants(command, activeLayout,
793 VK_SHADER_STAGE_VERTEX_BIT, 0, size, data);
800 auto* buffer =
new VulkanBuffer(device, physicalDevice, size, type,
801 commands, graphicsQueue);
802 if (!buffer->Initialize(data)) {
812 if (shader->compile(shaderSource)) {
821 auto* texture =
new VulkanTexture(device, physicalDevice, commands,
823 if (texture->LoadFromFile(TexturePath)) {
824 WriteTextureDescriptors(texture);
835 auto* texture =
new VulkanTexture(device, physicalDevice, commands,
847 const std::array<std::string, 6>& facePaths) {
849 commands, graphicsQueue);
850 if (texture->LoadCubemap(facePaths)) {
852 if (skyboxPipeline == VK_NULL_HANDLE) {
853 if (!CreateSkyboxPipeline()) {
854 SLEAK_ERROR(
"VulkanRenderer: Failed to create skybox pipeline");
861 m_skyboxCubemapView = texture->GetImageView();
862 m_skyboxCubemapSampler = texture->GetSampler();
863 for (
size_t i = 0; i < skyboxDescriptorSets.size(); i++) {
864 VkDescriptorImageInfo imageInfo{};
865 imageInfo.imageLayout =
866 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
867 imageInfo.imageView = m_skyboxCubemapView;
868 imageInfo.sampler = m_skyboxCubemapSampler;
870 VkWriteDescriptorSet descriptorWrite{};
871 descriptorWrite.sType =
872 VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
873 descriptorWrite.dstSet = skyboxDescriptorSets[i];
874 descriptorWrite.dstBinding = 0;
875 descriptorWrite.dstArrayElement = 0;
876 descriptorWrite.descriptorType =
877 VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
878 descriptorWrite.descriptorCount = 1;
879 descriptorWrite.pImageInfo = &imageInfo;
881 vkUpdateDescriptorSets(device, 1, &descriptorWrite, 0,
884 m_skyboxDescriptorsWritten =
true;
895 const std::string& panoramaPath) {
897 commands, graphicsQueue);
898 if (texture->LoadEquirectangular(panoramaPath)) {
900 if (skyboxPipeline == VK_NULL_HANDLE) {
901 if (!CreateSkyboxPipeline()) {
902 SLEAK_ERROR(
"VulkanRenderer: Failed to create skybox pipeline");
909 m_skyboxCubemapView = texture->GetImageView();
910 m_skyboxCubemapSampler = texture->GetSampler();
911 for (
size_t i = 0; i < skyboxDescriptorSets.size(); i++) {
912 VkDescriptorImageInfo imageInfo{};
913 imageInfo.imageLayout =
914 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
915 imageInfo.imageView = m_skyboxCubemapView;
916 imageInfo.sampler = m_skyboxCubemapSampler;
918 VkWriteDescriptorSet descriptorWrite{};
919 descriptorWrite.sType =
920 VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
921 descriptorWrite.dstSet = skyboxDescriptorSets[i];
922 descriptorWrite.dstBinding = 0;
923 descriptorWrite.dstArrayElement = 0;
924 descriptorWrite.descriptorType =
925 VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
926 descriptorWrite.descriptorCount = 1;
927 descriptorWrite.pImageInfo = &imageInfo;
929 vkUpdateDescriptorSets(device, 1, &descriptorWrite, 0,
932 m_skyboxDescriptorsWritten =
true;
945 if (!bFrameStarted)
return;
946 if (!texture.
IsValid() || slot != 0)
954 if (!vkTex || !vkTex->HasDescriptorSets())
958 if (CurrentFrameIndex < sets.size()) {
964 if (m_inGeometryPass)
966 vkCmdBindDescriptorSets(
967 command, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLay, 0, 1,
968 &sets[CurrentFrameIndex], 0,
nullptr);
974 if (!bFrameStarted)
return;
975 if (!texture || slot != 0)
983 if (!vkTex || !vkTex->HasDescriptorSets())
987 if (CurrentFrameIndex < sets.size()) {
993 if (m_inGeometryPass)
995 vkCmdBindDescriptorSets(
996 command, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLay, 0, 1,
997 &sets[CurrentFrameIndex], 0,
nullptr);
1002bool VulkanRenderer::CreateCommandBuffer() {
1003 commandBuffers.resize(MAX_FRAMES_IN_FLIGHT);
1005 VkCommandBufferAllocateInfo allocInfo{};
1006 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1007 allocInfo.commandPool = commands;
1008 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1009 allocInfo.commandBufferCount = MAX_FRAMES_IN_FLIGHT;
1011 if (vkAllocateCommandBuffers(device, &allocInfo,
1012 commandBuffers.data()) != VK_SUCCESS)
1020 if (device) vkDeviceWaitIdle(device);
1026 m_transferSemaphores[currentFrame]);
1047 vkDeviceWaitIdle(device);
1057 ImGui_ImplVulkan_Shutdown();
1058 ImGui_ImplSDL3_Shutdown();
1059 ImGui::DestroyContext();
1062 if (imguiDescriptorPool) {
1063 vkDestroyDescriptorPool(device, imguiDescriptorPool,
nullptr);
1064 imguiDescriptorPool = VK_NULL_HANDLE;
1068 if (descriptorPool) {
1069 vkDestroyDescriptorPool(device, descriptorPool,
nullptr);
1070 descriptorPool = VK_NULL_HANDLE;
1072 descriptorSets.clear();
1075 if (skyboxPipeline) {
1076 vkDestroyPipeline(device, skyboxPipeline,
nullptr);
1077 skyboxPipeline = VK_NULL_HANDLE;
1079 if (skyboxDescriptorPool) {
1080 vkDestroyDescriptorPool(device, skyboxDescriptorPool,
nullptr);
1081 skyboxDescriptorPool = VK_NULL_HANDLE;
1083 skyboxDescriptorSets.clear();
1084 delete skyboxShader;
1085 skyboxShader =
nullptr;
1088 if (skinnedPipeline) {
1089 vkDestroyPipeline(device, skinnedPipeline,
nullptr);
1090 skinnedPipeline = VK_NULL_HANDLE;
1092 delete skinnedShader;
1093 skinnedShader =
nullptr;
1096 if (debugLinePipeline) {
1097 vkDestroyPipeline(device, debugLinePipeline,
nullptr);
1098 debugLinePipeline = VK_NULL_HANDLE;
1100 delete debugLineShader;
1101 debugLineShader =
nullptr;
1104 DestroyCustomFormatPipelines();
1107 CleanupMSAAColorResources();
1110 CleanupGBufferResources();
1113 CleanupShadowResources();
1118 delete m_gbufferShader; m_gbufferShader =
nullptr;
1119 delete m_lightingShader; m_lightingShader =
nullptr;
1120 delete m_ssaoShader; m_ssaoShader =
nullptr;
1121 delete m_ssaoBlurShader; m_ssaoBlurShader =
nullptr;
1122 delete m_ssrShader; m_ssrShader =
nullptr;
1123 delete m_taaShader; m_taaShader =
nullptr;
1124 delete m_bloomThresholdShader; m_bloomThresholdShader =
nullptr;
1125 delete m_bloomDownsampleShader; m_bloomDownsampleShader =
nullptr;
1126 delete m_bloomUpsampleShader; m_bloomUpsampleShader =
nullptr;
1127 delete m_bloomCompositeShader; m_bloomCompositeShader =
nullptr;
1130 CleanupBoneUBOResources();
1133 if (m_shadowSamplerDescriptorSetLayout) {
1134 vkDestroyDescriptorSetLayout(device, m_shadowSamplerDescriptorSetLayout,
nullptr);
1135 m_shadowSamplerDescriptorSetLayout = VK_NULL_HANDLE;
1137 if (m_lightUBODescriptorSetLayout) {
1138 vkDestroyDescriptorSetLayout(device, m_lightUBODescriptorSetLayout,
nullptr);
1139 m_lightUBODescriptorSetLayout = VK_NULL_HANDLE;
1141 if (boneDescriptorSetLayout) {
1142 vkDestroyDescriptorSetLayout(device, boneDescriptorSetLayout,
nullptr);
1143 boneDescriptorSetLayout = VK_NULL_HANDLE;
1145 if (descriptorSetLayout) {
1146 vkDestroyDescriptorSetLayout(device, descriptorSetLayout,
nullptr);
1147 descriptorSetLayout = VK_NULL_HANDLE;
1151 CleanupDepthResources();
1154 if (swapChain && device) {
1155 vkDestroySwapchainKHR(device, swapChain,
nullptr);
1156 swapChain = VK_NULL_HANDLE;
1160 for (
auto& buffer : swapChainFramebuffers) {
1162 vkDestroyFramebuffer(device, buffer,
nullptr);
1163 buffer = VK_NULL_HANDLE;
1166 swapChainFramebuffers.clear();
1169 for (
auto& imgView : swapChainImageViews) {
1171 vkDestroyImageView(device, imgView,
nullptr);
1172 imgView = VK_NULL_HANDLE;
1175 swapChainImageViews.clear();
1178 for (
auto& sem : imageAvailableSemaphores) {
1179 if (sem) vkDestroySemaphore(device, sem,
nullptr);
1181 imageAvailableSemaphores.clear();
1183 for (
auto& sem : renderFinishedSemaphores) {
1184 if (sem) vkDestroySemaphore(device, sem,
nullptr);
1186 renderFinishedSemaphores.clear();
1188 for (
auto& fence : inFlightFences) {
1189 if (fence) vkDestroyFence(device, fence,
nullptr);
1191 inFlightFences.clear();
1194 for (uint32_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
1195 if (m_transferSemaphores[i]) {
1196 vkDestroySemaphore(device, m_transferSemaphores[i],
nullptr);
1197 m_transferSemaphores[i] = VK_NULL_HANDLE;
1199 auto& af = m_asyncFlush[i];
1200 if (af.commandBuffer != VK_NULL_HANDLE) {
1201 vkFreeCommandBuffers(af.device, af.commandPool, 1,
1204 for (
auto& pending : af.stagingBuffers) {
1214 delete simpleShader;
1215 simpleShader =
nullptr;
1220 vkDestroyPipeline(device, pipeline,
nullptr);
1221 pipeline = VK_NULL_HANDLE;
1226 vkDestroyPipelineLayout(device, pipelineLay,
nullptr);
1227 pipelineLay = VK_NULL_HANDLE;
1232 vkDestroyRenderPass(device, renderPass,
nullptr);
1233 renderPass = VK_NULL_HANDLE;
1237 if (m_defaultTexture) {
1238 delete m_defaultTexture;
1239 m_defaultTexture =
nullptr;
1244 vkDestroyCommandPool(device, commands,
nullptr);
1245 commands = VK_NULL_HANDLE;
1249 if (surface && instance) {
1250 vkDestroySurfaceKHR(instance, surface,
nullptr);
1251 surface = VK_NULL_HANDLE;
1255 if (debugMessenger && vkDestroyDebugUtilsMessengerEXT) {
1256 vkDestroyDebugUtilsMessengerEXT(instance, debugMessenger,
nullptr);
1257 debugMessenger = VK_NULL_HANDLE;
1267 vkDestroyDevice(device,
nullptr);
1268 device = VK_NULL_HANDLE;
1273 vkDestroyInstance(instance,
nullptr);
1274 instance = VK_NULL_HANDLE;
1281 RecreateSwapChain();
1287bool VulkanRenderer::RecreateSwapChain() {
1288 vkDeviceWaitIdle(device);
1292 bool hadGBuffer = m_gbufferResourcesCreated;
1293 CleanupGBufferResources();
1295 CleanupMSAAColorResources();
1298 if (!CreateSwapChain()) {
1302 if (!CreateImageViews()) {
1306 if (!CreateDepthResources()) {
1307 SLEAK_ERROR(
"Failed to recreate depth resources!");
1310 if (!CreateMSAAColorResources()) {
1311 SLEAK_ERROR(
"Failed to recreate MSAA color resources!");
1314 if (!CreateFrameBuffer()) {
1320 imagesInFlight.resize(swapChainImages.size(), VK_NULL_HANDLE);
1324 if (!CreateGBufferResources())
1325 SLEAK_WARN(
"RecreateSwapChain: Failed to recreate GBuffer resources!");
1343 case 1: m_msaaSamples = VK_SAMPLE_COUNT_1_BIT;
break;
1344 case 2: m_msaaSamples = VK_SAMPLE_COUNT_2_BIT;
break;
1345 case 4: m_msaaSamples = VK_SAMPLE_COUNT_4_BIT;
break;
1346 case 8: m_msaaSamples = VK_SAMPLE_COUNT_8_BIT;
break;
1347 default: m_msaaSamples = VK_SAMPLE_COUNT_1_BIT;
break;
1350 SLEAK_INFO(
"Applying MSAA change: {}x", newCount);
1352 vkDeviceWaitIdle(device);
1356 vkDestroyRenderPass(device, renderPass,
nullptr);
1357 renderPass = VK_NULL_HANDLE;
1362 vkDestroyPipeline(device, pipeline,
nullptr);
1363 pipeline = VK_NULL_HANDLE;
1365 if (skyboxPipeline) {
1366 vkDestroyPipeline(device, skyboxPipeline,
nullptr);
1367 skyboxPipeline = VK_NULL_HANDLE;
1369 if (skinnedPipeline) {
1370 vkDestroyPipeline(device, skinnedPipeline,
nullptr);
1371 skinnedPipeline = VK_NULL_HANDLE;
1373 if (debugLinePipeline) {
1374 vkDestroyPipeline(device, debugLinePipeline,
nullptr);
1375 debugLinePipeline = VK_NULL_HANDLE;
1379 DestroyCustomFormatPipelines();
1382 CleanupGBufferResources();
1386 ImGui_ImplVulkan_Shutdown();
1387 ImGui_ImplSDL3_Shutdown();
1388 ImGui::DestroyContext();
1393 CleanupMSAAColorResources();
1399 CreateDepthResources();
1400 CreateMSAAColorResources();
1402 CreateFrameBuffer();
1403 CreateGraphicsPipeline();
1404 CreateSkyboxPipeline();
1405 CreateSkinnedPipeline();
1406 CreateDebugLinePipeline();
1411 if (m_skyboxCubemapView != VK_NULL_HANDLE && m_skyboxCubemapSampler != VK_NULL_HANDLE) {
1412 for (
size_t i = 0; i < skyboxDescriptorSets.size(); i++) {
1413 VkDescriptorImageInfo imageInfo{};
1414 imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1415 imageInfo.imageView = m_skyboxCubemapView;
1416 imageInfo.sampler = m_skyboxCubemapSampler;
1418 VkWriteDescriptorSet descriptorWrite{};
1419 descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1420 descriptorWrite.dstSet = skyboxDescriptorSets[i];
1421 descriptorWrite.dstBinding = 0;
1422 descriptorWrite.dstArrayElement = 0;
1423 descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1424 descriptorWrite.descriptorCount = 1;
1425 descriptorWrite.pImageInfo = &imageInfo;
1427 vkUpdateDescriptorSets(device, 1, &descriptorWrite, 0,
nullptr);
1429 m_skyboxDescriptorsWritten =
true;
1432 SLEAK_INFO(
"MSAA change applied successfully");
1440 RecreateSwapChain();
1455bool VulkanRenderer::CreateCommandPool() {
1456 VkCommandPoolCreateInfo poolInfo{};
1457 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
1458 poolInfo.queueFamilyIndex = QueueIDs.GraphicsIndex;
1459 poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
1461 if (vkCreateCommandPool(device, &poolInfo,
nullptr, &commands) !=
1470bool VulkanRenderer::CreateSyncObjects() {
1471 uint32_t imageCount =
static_cast<uint32_t
>(swapChainImages.size());
1475 imageAvailableSemaphores.resize(imageCount);
1476 renderFinishedSemaphores.resize(imageCount);
1477 inFlightFences.resize(MAX_FRAMES_IN_FLIGHT);
1478 imagesInFlight.resize(imageCount, VK_NULL_HANDLE);
1480 VkSemaphoreCreateInfo semaphoreInfo{};
1481 semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
1483 VkFenceCreateInfo fenceInfo{};
1484 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
1485 fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
1488 for (uint32_t i = 0; i < imageCount; i++) {
1489 if (vkCreateSemaphore(device, &semaphoreInfo,
nullptr,
1490 &imageAvailableSemaphores[i]) != VK_SUCCESS ||
1491 vkCreateSemaphore(device, &semaphoreInfo,
nullptr,
1492 &renderFinishedSemaphores[i]) != VK_SUCCESS) {
1498 for (uint32_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
1499 if (vkCreateSemaphore(device, &semaphoreInfo,
nullptr,
1500 &m_transferSemaphores[i]) != VK_SUCCESS ||
1501 vkCreateFence(device, &fenceInfo,
nullptr,
1502 &inFlightFences[i]) != VK_SUCCESS) {
1505 m_asyncFlush[i] = {};
1508 m_semaphoreIndex = 0;
1521 vkDeviceWaitIdle(device);
1523 vkDestroyFramebuffer(device, m_shadowFramebuffer,
nullptr);
1524 m_shadowFramebuffer = VK_NULL_HANDLE;
1525 vkDestroyImageView(device, m_shadowImageView,
nullptr);
1526 m_shadowImageView = VK_NULL_HANDLE;
1527 vkDestroyImage(device, m_shadowImage,
nullptr);
1528 m_shadowImage = VK_NULL_HANDLE;
1529 vkFreeMemory(device, m_shadowImageMemory,
nullptr);
1530 m_shadowImageMemory = VK_NULL_HANDLE;
1534 VkImageCreateInfo imageInfo{};
1535 imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
1536 imageInfo.imageType = VK_IMAGE_TYPE_2D;
1538 imageInfo.mipLevels = 1;
1539 imageInfo.arrayLayers = 1;
1540 imageInfo.format = VK_FORMAT_D32_SFLOAT;
1541 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
1542 imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1543 imageInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT |
1544 VK_IMAGE_USAGE_SAMPLED_BIT;
1545 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
1546 imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
1548 if (vkCreateImage(device, &imageInfo,
nullptr, &m_shadowImage) !=
1550 SLEAK_ERROR(
"Shadow resolution change: image creation failed");
1555 VkMemoryRequirements memReqs;
1556 vkGetImageMemoryRequirements(device, m_shadowImage, &memReqs);
1558 VkMemoryAllocateInfo allocInfo{};
1559 allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
1560 allocInfo.allocationSize = memReqs.size;
1561 allocInfo.memoryTypeIndex = FindMemoryType(
1562 memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
1564 if (vkAllocateMemory(device, &allocInfo,
nullptr, &m_shadowImageMemory) !=
1566 SLEAK_ERROR(
"Shadow resolution change: memory allocation failed");
1570 vkBindImageMemory(device, m_shadowImage, m_shadowImageMemory, 0);
1572 VkImageViewCreateInfo viewInfo{};
1573 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
1574 viewInfo.image = m_shadowImage;
1575 viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
1576 viewInfo.format = VK_FORMAT_D32_SFLOAT;
1577 viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
1578 viewInfo.subresourceRange.baseMipLevel = 0;
1579 viewInfo.subresourceRange.levelCount = 1;
1580 viewInfo.subresourceRange.baseArrayLayer = 0;
1581 viewInfo.subresourceRange.layerCount = 1;
1583 if (vkCreateImageView(device, &viewInfo,
nullptr, &m_shadowImageView) !=
1585 SLEAK_ERROR(
"Shadow resolution change: image view creation failed");
1590 VkFramebufferCreateInfo fbInfo{};
1591 fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
1592 fbInfo.renderPass = m_shadowRenderPass;
1593 fbInfo.attachmentCount = 1;
1594 fbInfo.pAttachments = &m_shadowImageView;
1599 if (vkCreateFramebuffer(device, &fbInfo,
nullptr, &m_shadowFramebuffer) !=
1601 SLEAK_ERROR(
"Shadow resolution change: framebuffer creation failed");
1608 VkCommandBufferAllocateInfo cmdAllocInfo{};
1609 cmdAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1610 cmdAllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1611 cmdAllocInfo.commandPool = commands;
1612 cmdAllocInfo.commandBufferCount = 1;
1614 VkCommandBuffer cmdBuf;
1615 vkAllocateCommandBuffers(device, &cmdAllocInfo, &cmdBuf);
1617 VkCommandBufferBeginInfo cmdBeginInfo{};
1618 cmdBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
1619 cmdBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
1620 vkBeginCommandBuffer(cmdBuf, &cmdBeginInfo);
1622 VkImageMemoryBarrier barrier{};
1623 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
1624 barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1625 barrier.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
1626 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1627 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1628 barrier.image = m_shadowImage;
1629 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
1630 barrier.subresourceRange.baseMipLevel = 0;
1631 barrier.subresourceRange.levelCount = 1;
1632 barrier.subresourceRange.baseArrayLayer = 0;
1633 barrier.subresourceRange.layerCount = 1;
1634 barrier.srcAccessMask = 0;
1635 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
1637 vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
1638 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0,
1639 nullptr, 0,
nullptr, 1, &barrier);
1641 vkEndCommandBuffer(cmdBuf);
1643 VkSubmitInfo layoutSubmit{};
1644 layoutSubmit.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
1645 layoutSubmit.commandBufferCount = 1;
1646 layoutSubmit.pCommandBuffers = &cmdBuf;
1648 VkFenceCreateInfo layoutFenceInfo{};
1649 layoutFenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
1650 VkFence layoutFence;
1651 vkCreateFence(device, &layoutFenceInfo,
nullptr, &layoutFence);
1652 vkQueueSubmit(graphicsQueue, 1, &layoutSubmit, layoutFence);
1653 vkWaitForFences(device, 1, &layoutFence, VK_TRUE, UINT64_MAX);
1654 vkDestroyFence(device, layoutFence,
nullptr);
1655 vkFreeCommandBuffers(device, commands, 1, &cmdBuf);
1659 if (m_lightUBOCreated && m_shadowImageView && m_shadowSampler &&
1660 m_shadowRawSampler) {
1661 for (uint32_t i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) {
1662 VkDescriptorImageInfo compareInfo{};
1663 compareInfo.imageLayout =
1664 VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
1665 compareInfo.imageView = m_shadowImageView;
1666 compareInfo.sampler = m_shadowSampler;
1668 VkDescriptorImageInfo rawInfo{};
1669 rawInfo.imageLayout =
1670 VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
1671 rawInfo.imageView = m_shadowImageView;
1672 rawInfo.sampler = m_shadowRawSampler;
1674 std::array<VkWriteDescriptorSet, 2> writes{};
1675 writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1676 writes[0].dstSet = m_shadowSamplerDescriptorSets[i];
1677 writes[0].dstBinding = 0;
1678 writes[0].dstArrayElement = 0;
1679 writes[0].descriptorType =
1680 VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1681 writes[0].descriptorCount = 1;
1682 writes[0].pImageInfo = &compareInfo;
1684 writes[1] = writes[0];
1685 writes[1].dstBinding = 1;
1686 writes[1].pImageInfo = &rawInfo;
1688 vkUpdateDescriptorSets(device,
1689 static_cast<uint32_t
>(writes.size()),
1690 writes.data(), 0,
nullptr);
1694 SLEAK_INFO(
"VulkanRenderer: Shadow map resized to {}x{}",
1701bool VulkanRenderer::CreateGBufferResources() {
1702 if (m_gbufferResourcesCreated)
return true;
1705 for (uint32_t i = 0; i < GBUFFER_COUNT; ++i) {
1706 VkImageCreateInfo imageInfo{};
1707 imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
1708 imageInfo.imageType = VK_IMAGE_TYPE_2D;
1709 imageInfo.extent.width = scExtent.width;
1710 imageInfo.extent.height = scExtent.height;
1711 imageInfo.extent.depth = 1;
1712 imageInfo.mipLevels = 1;
1713 imageInfo.arrayLayers = 1;
1714 imageInfo.format = m_gbufferFormats[i];
1715 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
1716 imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1717 imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
1718 | VK_IMAGE_USAGE_SAMPLED_BIT;
1719 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
1720 imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
1722 if (vkCreateImage(device, &imageInfo,
nullptr, &m_gbufferImages[i]) != VK_SUCCESS) {
1723 SLEAK_ERROR(
"GBuffer: Failed to create GBuffer image {}!", i);
1727 VkMemoryRequirements memReqs;
1728 vkGetImageMemoryRequirements(device, m_gbufferImages[i], &memReqs);
1730 VkMemoryAllocateInfo allocInfo{};
1731 allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
1732 allocInfo.allocationSize = memReqs.size;
1733 allocInfo.memoryTypeIndex = FindMemoryType(memReqs.memoryTypeBits,
1734 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
1736 if (vkAllocateMemory(device, &allocInfo,
nullptr, &m_gbufferMemory[i]) != VK_SUCCESS) {
1737 SLEAK_ERROR(
"GBuffer: Failed to allocate GBuffer memory {}!", i);
1740 vkBindImageMemory(device, m_gbufferImages[i], m_gbufferMemory[i], 0);
1742 VkImageViewCreateInfo viewInfo{};
1743 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
1744 viewInfo.image = m_gbufferImages[i];
1745 viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
1746 viewInfo.format = m_gbufferFormats[i];
1747 viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1748 viewInfo.subresourceRange.baseMipLevel = 0;
1749 viewInfo.subresourceRange.levelCount = 1;
1750 viewInfo.subresourceRange.baseArrayLayer = 0;
1751 viewInfo.subresourceRange.layerCount = 1;
1753 if (vkCreateImageView(device, &viewInfo,
nullptr, &m_gbufferViews[i]) != VK_SUCCESS) {
1754 SLEAK_ERROR(
"GBuffer: Failed to create GBuffer image view {}!", i);
1759 if (!CreateGBufferRenderPass()) {
SLEAK_ERROR(
"GBuffer: render pass failed!");
return false; }
1760 if (!CreateGBufferFramebuffer()) {
SLEAK_ERROR(
"GBuffer: framebuffer failed!");
return false; }
1765 if (!CreateSSAOResources()) {
SLEAK_ERROR(
"GBuffer: SSAO resources failed!");
return false; }
1766 if (!CreateBloomResources()) {
SLEAK_ERROR(
"GBuffer: bloom/HDR resources failed!");
return false; }
1769 if (!CreateSSRResources()) {
SLEAK_ERROR(
"GBuffer: SSR resources failed!");
return false; }
1772 m_ssaoFallbackPrimed =
false;
1773 m_ssrFallbackPrimed =
false;
1774 m_bloomFallbackPrimed =
false;
1775 InitDisabledEffectFallbacks();
1776 if (!CreateGBufferDescriptorSets()) {
SLEAK_ERROR(
"GBuffer: descriptor sets failed!");
return false; }
1777 if (!CreateDeferredCBResources()) {
SLEAK_ERROR(
"GBuffer: deferred CB failed!");
return false; }
1778 if (!CreatePBRMaterialResources()) {
SLEAK_ERROR(
"GBuffer: PBR material resources failed!");
return false; }
1779 if (!CreateIBLResources()) {
SLEAK_ERROR(
"GBuffer: IBL resources failed!");
return false; }
1780 if (!CreateLightingRenderPass()) {
SLEAK_ERROR(
"GBuffer: lighting RP failed!");
return false; }
1781 if (!CreateLightingFramebuffers()) {
SLEAK_ERROR(
"GBuffer: lighting FBs failed!");
return false; }
1782 if (!CreateLightingPipeline()) {
SLEAK_ERROR(
"GBuffer: lighting pipeline failed!");
return false; }
1783 if (!CreateForwardRenderPass()) {
SLEAK_ERROR(
"GBuffer: forward RP failed!");
return false; }
1784 if (!CreateForwardFramebuffers()) {
SLEAK_ERROR(
"GBuffer: forward FBs failed!");
return false; }
1785 if (!CreateGBufferPipeline()) {
SLEAK_ERROR(
"GBuffer: gbuffer pipeline failed!");
return false; }
1786 if (!CreateSkinnedGbufferPipeline()) {
SLEAK_ERROR(
"GBuffer: skinned gbuffer pipeline failed!");
return false; }
1790 UpdateSSAODescriptors();
1793 UpdateSSRDescriptors();
1798 if (m_forwardRenderPass != VK_NULL_HANDLE) {
1799 if (pipeline) { vkDestroyPipeline(device, pipeline,
nullptr); pipeline = VK_NULL_HANDLE; }
1800 if (skyboxPipeline) { vkDestroyPipeline(device, skyboxPipeline,
nullptr); skyboxPipeline = VK_NULL_HANDLE; }
1801 if (debugLinePipeline) { vkDestroyPipeline(device, debugLinePipeline,
nullptr); debugLinePipeline = VK_NULL_HANDLE; }
1802 if (skinnedPipeline) { vkDestroyPipeline(device, skinnedPipeline,
nullptr); skinnedPipeline = VK_NULL_HANDLE; }
1803 if (m_skinnedGbufferPipeline) { vkDestroyPipeline(device, m_skinnedGbufferPipeline,
nullptr); m_skinnedGbufferPipeline = VK_NULL_HANDLE; }
1804 DestroyCustomFormatPipelines();
1806 if (skyboxDescriptorPool) {
1807 vkDestroyDescriptorPool(device, skyboxDescriptorPool,
nullptr);
1808 skyboxDescriptorPool = VK_NULL_HANDLE;
1810 skyboxDescriptorSets.clear();
1811 delete skyboxShader;
1812 skyboxShader =
nullptr;
1814 CreateGraphicsPipeline();
1815 CreateSkyboxPipeline();
1816 CreateDebugLinePipeline();
1817 CreateSkinnedPipeline();
1818 CreateSkinnedGbufferPipeline();
1820 m_gbufferResourcesCreated =
true;
1823 if (m_skyboxCubemapView != VK_NULL_HANDLE && m_skyboxCubemapSampler != VK_NULL_HANDLE) {
1824 for (
size_t i = 0; i < skyboxDescriptorSets.size(); i++) {
1825 VkDescriptorImageInfo imageInfo{};
1826 imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1827 imageInfo.imageView = m_skyboxCubemapView;
1828 imageInfo.sampler = m_skyboxCubemapSampler;
1830 VkWriteDescriptorSet descriptorWrite{};
1831 descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1832 descriptorWrite.dstSet = skyboxDescriptorSets[i];
1833 descriptorWrite.dstBinding = 0;
1834 descriptorWrite.dstArrayElement = 0;
1835 descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1836 descriptorWrite.descriptorCount = 1;
1837 descriptorWrite.pImageInfo = &imageInfo;
1839 vkUpdateDescriptorSets(device, 1, &descriptorWrite, 0,
nullptr);
1841 m_skyboxDescriptorsWritten =
true;
1845 SLEAK_INFO(
"VulkanRenderer: Deferred GBuffer resources created ({}x{})",
1846 scExtent.width, scExtent.height);
#define SLEAK_RETURN_ERR(...)
Backend-agnostic GPU buffer: vertex, index, constant, or resource view target.
static RenderCommandQueue * GetInstance()
Lazily creates and returns the process-wide singleton instance.
virtual void ConfigureRenderMode()=0
Applies the current RenderMode (fill/wireframe/points) to backend state.
virtual void ConfigureRenderFace()=0
Applies the current RenderFace (cull mode) to backend state.
bool m_vsyncChangeRequested
void UpdateFrameMetrics()
Rolls up frame count into rate/time/triangle stats once per MetricUpdateInterval.
uint32_t m_msaaSampleCount
uint32_t m_pendingMsaaSampleCount
void SetPerformanceCounter(bool value)
bool m_shadowResChangeRequested
bool m_msaaChangeRequested
uint32_t m_shadowMapResolution
bool m_shadowResourcesCreated
uint32_t m_pendingShadowMapResolution
static void RegisterCreateCubemapTextureFromPanorama(T *instance, Texture *(T::*method)(const std::string &))
static void RegisterCreateCubemapTexture(T *instance, Texture *(T::*method)(const std::array< std::string, 6 > &))
static void RegisterCreateBuffer(T *instance, BufferBase *(T::*method)(BufferType, uint32_t, void *))
static void RegisterCreateShader(T *instance, Shader *(T::*method)(const std::string &))
static void RegisterCreateTextureFromMemory(std::function< Texture *(const void *, uint32_t, uint32_t, TextureFormat, uint32_t)> func)
static void RegisterCreateTexture(T *instance, Texture *(T::*method)(const std::string &))
Backend-agnostic compiled shader program.
VMA-backed Vulkan buffer with staging uploads, batched copies, and a size-bucketed recycling pool.
static void AdvanceDeletionFrame()
void * GetData() override
static void ProcessDeferredDeletions(uint32_t maxFramesInFlight)
static VkDeviceSize GetTotalAllocatedBytes()
static void UntrackAllocation(VkDeviceSize size)
static void DestroyAllocator()
static void FlushAllDeferredDeletions()
VulkanBuffer(VkDevice device, VkPhysicalDevice physicalDevice, uint32_t size, BufferType type, VkCommandPool commandPool, VkQueue graphicsQueue)
static AsyncFlushResult FlushPendingCopiesAsync(VkSemaphore signalSemaphore)
static VmaAllocator GetAllocator()
static VkDeviceSize GetDeviceLocalHeapSize()
static void SetBatchingEnabled(bool enabled)
VulkanCubemapTexture(VkDevice device, VkPhysicalDevice physicalDevice, VkCommandPool commandPool, VkQueue graphicsQueue)
virtual size_t GetGPUMemoryBudget() const override
Returns the device-local heap size reported by the allocator.
virtual bool CreateImGUI() override
Initializes ImGui and its Vulkan backend against the active render pass.
virtual size_t GetGPUMemoryUsed() const override
Returns total bytes currently allocated by VulkanBuffer.
virtual void Draw(uint32_t vertexCount) override
Issues a non-indexed draw call and updates the vertex/triangle counters.
void ApplyShadowResolutionChange() override
Recreates the extent-dependent shadow map objects at the queued resolution.
virtual void SetViewport(float x, float y, float width, float height, float minDepth=0.0f, float maxDepth=1.0f) override
Sets the dynamic viewport on the active command buffer.
virtual void BindTexture(RefPtr< Sleak::Texture > texture, uint32_t slot=0) override
Binds a texture's descriptor set at slot 0, skipping cubemaps and the GBuffer geometry pass.
virtual void BindIndexBuffer(RefPtr< BufferBase > buffer, uint32_t slot=0) override
Binds a 32-bit index buffer.
virtual void ClearDepthStencil(bool clearDepth, bool clearStencil, float depth, uint8_t stencil) override
No-op; depth/stencil clears are driven by the render pass clear values.
virtual void DrawInstance(uint32_t instanceCount, uint32_t vertexPerInstance) override
Issues an instanced, non-indexed draw call.
virtual void FlushPendingTransfers() override
Kicks off the current frame's async buffer upload batch.
virtual void WaitIdle() override
Blocks until the device finishes all submitted GPU work.
virtual void SetRenderMode(RenderMode mode) override
Stores the polygon mode for the next pipeline rebuild (Vulkan state is baked).
virtual void BindTextureRaw(Sleak::Texture *texture, uint32_t slot=0) override
Raw-pointer variant of BindTexture.
virtual Shader * CreateShader(const std::string &shaderSource) override
Compiles a VulkanShader from source.
VulkanRenderer(Window *window)
virtual void Cleanup() override
Tears down every Vulkan resource in reverse dependency order.
virtual void DrawIndexed(uint32_t indexCount) override
Issues an indexed draw call and updates the vertex/triangle counters.
virtual BufferBase * CreateBuffer(BufferType Type, uint32_t size, void *data) override
Allocates and initializes a VulkanBuffer.
~VulkanRenderer()
Calls Cleanup() to tear down all Vulkan resources.
void ApplyVSyncChange() override
Recreates the swapchain to apply a queued VSync toggle.
void ApplyMSAAChange() override
virtual void BeginRender() override
virtual void EndCustomFormatPass() override
Restores the previous pipeline and descriptor set after custom-format draws.
virtual Texture * CreateTextureFromData(uint32_t width, uint32_t height, void *data) override
Loads a texture from an in-memory RGBA8 buffer.
virtual void BindConstantBuffer(RefPtr< BufferBase > buffer, uint32_t slot=0) override
virtual void EndRender() override
Ends the active render pass, submits the command buffer, and presents.
virtual Texture * CreateTexture(const std::string &TexturePath) override
Loads a texture from disk and writes its descriptor sets.
virtual void SetRenderFace(RenderFace face) override
Stores the cull face for the next pipeline rebuild (Vulkan state is baked).
Texture * CreateCubemapTexture(const std::array< std::string, 6 > &facePaths)
Loads a cubemap from six face images and writes it into the skybox descriptor sets.
virtual void DrawIndexedInstance(uint32_t instanceCount, uint32_t indexPerInstance) override
Issues an instanced, indexed draw call.
Texture * CreateCubemapTextureFromPanorama(const std::string &panoramaPath)
Loads an equirectangular panorama as a cubemap and writes it into the skybox descriptor sets.
virtual void ClearRenderTarget(float r, float g, float b, float a) override
Stores the clear color used by the next BeginRender.
virtual void Resize(uint32_t width, uint32_t height) override
Recreates the swapchain for the new window dimensions.
virtual void BindVertexBuffer(RefPtr< BufferBase > buffer, uint32_t slot=0) override
virtual bool Initialize() override
virtual void BeginCustomFormatPass(VertexFormatHandle format) override
Binds the custom-format pipeline matching the currently active render pass.
VulkanShader(VkDevice dev)
Vulkan 2D texture: image + view + sampler, with per-swapchain-image descriptor sets.
const std::vector< VkDescriptorSet > & GetDescriptorSets() const
VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, VkCommandPool commandPool, VkQueue graphicsQueue)
virtual bool IsValid() const
True if the pointer is non-null.
virtual TextureType GetType() const =0
Backend-facing rendering layer shared by the four graphics backends.
RenderMode
Rasterizer fill style for a draw.
BufferType
GPU buffer usage kind, drives backend binding flags and layout.
RenderFace
Which triangle winding gets culled.
Root namespace for everything the engine exposes.
uint32_t VertexFormatHandle