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;
603 if (!bFrameStarted)
return;
604 if (CustomFormatDrawsSuppressed())
return;
605 vkCmdDraw(command, vertexCount, 1, 0, 0);
606 if (!m_shadowPassActive) {
614 if (!bFrameStarted)
return;
615 if (CustomFormatDrawsSuppressed())
return;
616 vkCmdDrawIndexed(command, indexCount, 1, 0, 0, 0);
617 if (!m_shadowPassActive) {
625 uint32_t vertexPerInstance) {
626 if (!bFrameStarted)
return;
627 if (CustomFormatDrawsSuppressed())
return;
628 vkCmdDraw(command, vertexPerInstance, instanceCount, 0, 0);
633 uint32_t indexPerInstance) {
634 if (!bFrameStarted)
return;
635 if (CustomFormatDrawsSuppressed())
return;
636 vkCmdDrawIndexed(command, indexPerInstance, instanceCount, 0, 0, 0);
654 float height,
float minDepth,
656 if (!bFrameStarted)
return;
657 VkViewport viewport{};
660 viewport.width =
width;
662 viewport.minDepth = minDepth;
663 viewport.maxDepth = maxDepth;
664 vkCmdSetViewport(command, 0, 1, &viewport);
670 clearColor = {{r, g, b, a}};
675 float depth, uint8_t stencil) {
683 if (!bFrameStarted)
return;
688 if (wantFormat != 0) {
690 }
else if (m_activeCustomFormat != 0) {
694 VkBuffer buffers[] = {vkBuf->GetVkBuffer()};
695 VkDeviceSize offsets[] = {0};
696 vkCmdBindVertexBuffers(command, slot, 1, buffers, offsets);
702 if (!bFrameStarted)
return;
705 vkCmdBindIndexBuffer(command, vkBuf->GetVkBuffer(), 0,
706 VK_INDEX_TYPE_UINT32);
713 if (!bFrameStarted)
return;
721 uint32_t size =
static_cast<uint32_t
>(vkBuf->GetSize());
722 if (size > 128) size = 128;
726 VkPipelineLayout activeLayout = (m_inGeometryPass && m_gbufferGeomLayout != VK_NULL_HANDLE)
727 ? m_gbufferGeomLayout : pipelineLay;
732 if (m_inGeometryPass && !m_shadowPassActive &&
m_taaEnabled &&
733 (m_taaJitter[0] != 0.0f || m_taaJitter[1] != 0.0f) && size >= 64) {
735 memcpy(jdata, data, size);
736 const float jx = m_taaJitter[0] * 2.0f;
737 const float jy = -m_taaJitter[1] * 2.0f;
740 for (
int r = 0; r < 4; ++r) {
741 jdata[r * 4 + 0] += jx * jdata[r * 4 + 3];
742 jdata[r * 4 + 1] += jy * jdata[r * 4 + 3];
744 vkCmdPushConstants(command, activeLayout,
745 VK_SHADER_STAGE_VERTEX_BIT, 0, size, jdata);
749 if (m_shadowPassActive && slot == 0 && size >= 128) {
754 const float* srcWorld =
reinterpret_cast<const float*
>(
755 static_cast<const char*
>(data) + 64);
757 if (!m_shadowPCCacheValid ||
758 memcmp(srcWorld, m_shadowWorldCache, 64) != 0) {
760 for (
int r = 0; r < 4; ++r) {
761 for (
int c = 0; c < 4; ++c) {
763 for (
int k = 0; k < 4; ++k) {
764 sum += srcWorld[r * 4 + k] * m_lightVP[k * 4 + c];
766 m_shadowPCCache[r * 4 + c] = sum;
769 memcpy(&m_shadowPCCache[16], srcWorld, 64);
770 memcpy(m_shadowWorldCache, srcWorld, 64);
771 m_shadowPCCacheValid =
true;
774 vkCmdPushConstants(command, activeLayout,
775 VK_SHADER_STAGE_VERTEX_BIT, 0, 128, m_shadowPCCache);
777 vkCmdPushConstants(command, activeLayout,
778 VK_SHADER_STAGE_VERTEX_BIT, 0, size, data);
785 auto* buffer =
new VulkanBuffer(device, physicalDevice, size, type,
786 commands, graphicsQueue);
787 if (!buffer->Initialize(data)) {
797 if (shader->compile(shaderSource)) {
806 auto* texture =
new VulkanTexture(device, physicalDevice, commands,
808 if (texture->LoadFromFile(TexturePath)) {
809 WriteTextureDescriptors(texture);
820 auto* texture =
new VulkanTexture(device, physicalDevice, commands,
832 const std::array<std::string, 6>& facePaths) {
834 commands, graphicsQueue);
835 if (texture->LoadCubemap(facePaths)) {
837 if (skyboxPipeline == VK_NULL_HANDLE) {
838 if (!CreateSkyboxPipeline()) {
839 SLEAK_ERROR(
"VulkanRenderer: Failed to create skybox pipeline");
846 m_skyboxCubemapView = texture->GetImageView();
847 m_skyboxCubemapSampler = texture->GetSampler();
848 for (
size_t i = 0; i < skyboxDescriptorSets.size(); i++) {
849 VkDescriptorImageInfo imageInfo{};
850 imageInfo.imageLayout =
851 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
852 imageInfo.imageView = m_skyboxCubemapView;
853 imageInfo.sampler = m_skyboxCubemapSampler;
855 VkWriteDescriptorSet descriptorWrite{};
856 descriptorWrite.sType =
857 VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
858 descriptorWrite.dstSet = skyboxDescriptorSets[i];
859 descriptorWrite.dstBinding = 0;
860 descriptorWrite.dstArrayElement = 0;
861 descriptorWrite.descriptorType =
862 VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
863 descriptorWrite.descriptorCount = 1;
864 descriptorWrite.pImageInfo = &imageInfo;
866 vkUpdateDescriptorSets(device, 1, &descriptorWrite, 0,
869 m_skyboxDescriptorsWritten =
true;
880 const std::string& panoramaPath) {
882 commands, graphicsQueue);
883 if (texture->LoadEquirectangular(panoramaPath)) {
885 if (skyboxPipeline == VK_NULL_HANDLE) {
886 if (!CreateSkyboxPipeline()) {
887 SLEAK_ERROR(
"VulkanRenderer: Failed to create skybox pipeline");
894 m_skyboxCubemapView = texture->GetImageView();
895 m_skyboxCubemapSampler = texture->GetSampler();
896 for (
size_t i = 0; i < skyboxDescriptorSets.size(); i++) {
897 VkDescriptorImageInfo imageInfo{};
898 imageInfo.imageLayout =
899 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
900 imageInfo.imageView = m_skyboxCubemapView;
901 imageInfo.sampler = m_skyboxCubemapSampler;
903 VkWriteDescriptorSet descriptorWrite{};
904 descriptorWrite.sType =
905 VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
906 descriptorWrite.dstSet = skyboxDescriptorSets[i];
907 descriptorWrite.dstBinding = 0;
908 descriptorWrite.dstArrayElement = 0;
909 descriptorWrite.descriptorType =
910 VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
911 descriptorWrite.descriptorCount = 1;
912 descriptorWrite.pImageInfo = &imageInfo;
914 vkUpdateDescriptorSets(device, 1, &descriptorWrite, 0,
917 m_skyboxDescriptorsWritten =
true;
930 if (!bFrameStarted)
return;
931 if (!texture.
IsValid() || slot != 0)
939 if (!vkTex || !vkTex->HasDescriptorSets())
943 if (CurrentFrameIndex < sets.size()) {
949 if (m_inGeometryPass)
951 vkCmdBindDescriptorSets(
952 command, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLay, 0, 1,
953 &sets[CurrentFrameIndex], 0,
nullptr);
959 if (!bFrameStarted)
return;
960 if (!texture || slot != 0)
968 if (!vkTex || !vkTex->HasDescriptorSets())
972 if (CurrentFrameIndex < sets.size()) {
978 if (m_inGeometryPass)
980 vkCmdBindDescriptorSets(
981 command, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLay, 0, 1,
982 &sets[CurrentFrameIndex], 0,
nullptr);
987bool VulkanRenderer::CreateCommandBuffer() {
988 commandBuffers.resize(MAX_FRAMES_IN_FLIGHT);
990 VkCommandBufferAllocateInfo allocInfo{};
991 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
992 allocInfo.commandPool = commands;
993 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
994 allocInfo.commandBufferCount = MAX_FRAMES_IN_FLIGHT;
996 if (vkAllocateCommandBuffers(device, &allocInfo,
997 commandBuffers.data()) != VK_SUCCESS)
1005 if (device) vkDeviceWaitIdle(device);
1011 m_transferSemaphores[currentFrame]);
1032 vkDeviceWaitIdle(device);
1042 ImGui_ImplVulkan_Shutdown();
1043 ImGui_ImplSDL3_Shutdown();
1044 ImGui::DestroyContext();
1047 if (imguiDescriptorPool) {
1048 vkDestroyDescriptorPool(device, imguiDescriptorPool,
nullptr);
1049 imguiDescriptorPool = VK_NULL_HANDLE;
1053 if (descriptorPool) {
1054 vkDestroyDescriptorPool(device, descriptorPool,
nullptr);
1055 descriptorPool = VK_NULL_HANDLE;
1057 descriptorSets.clear();
1060 if (skyboxPipeline) {
1061 vkDestroyPipeline(device, skyboxPipeline,
nullptr);
1062 skyboxPipeline = VK_NULL_HANDLE;
1064 if (skyboxDescriptorPool) {
1065 vkDestroyDescriptorPool(device, skyboxDescriptorPool,
nullptr);
1066 skyboxDescriptorPool = VK_NULL_HANDLE;
1068 skyboxDescriptorSets.clear();
1069 delete skyboxShader;
1070 skyboxShader =
nullptr;
1073 if (skinnedPipeline) {
1074 vkDestroyPipeline(device, skinnedPipeline,
nullptr);
1075 skinnedPipeline = VK_NULL_HANDLE;
1077 delete skinnedShader;
1078 skinnedShader =
nullptr;
1081 if (debugLinePipeline) {
1082 vkDestroyPipeline(device, debugLinePipeline,
nullptr);
1083 debugLinePipeline = VK_NULL_HANDLE;
1085 delete debugLineShader;
1086 debugLineShader =
nullptr;
1089 DestroyCustomFormatPipelines();
1092 CleanupMSAAColorResources();
1095 CleanupGBufferResources();
1098 CleanupShadowResources();
1103 delete m_gbufferShader; m_gbufferShader =
nullptr;
1104 delete m_lightingShader; m_lightingShader =
nullptr;
1105 delete m_ssaoShader; m_ssaoShader =
nullptr;
1106 delete m_ssaoBlurShader; m_ssaoBlurShader =
nullptr;
1107 delete m_ssrShader; m_ssrShader =
nullptr;
1108 delete m_taaShader; m_taaShader =
nullptr;
1109 delete m_bloomThresholdShader; m_bloomThresholdShader =
nullptr;
1110 delete m_bloomDownsampleShader; m_bloomDownsampleShader =
nullptr;
1111 delete m_bloomUpsampleShader; m_bloomUpsampleShader =
nullptr;
1112 delete m_bloomCompositeShader; m_bloomCompositeShader =
nullptr;
1115 CleanupBoneUBOResources();
1118 if (m_shadowSamplerDescriptorSetLayout) {
1119 vkDestroyDescriptorSetLayout(device, m_shadowSamplerDescriptorSetLayout,
nullptr);
1120 m_shadowSamplerDescriptorSetLayout = VK_NULL_HANDLE;
1122 if (m_lightUBODescriptorSetLayout) {
1123 vkDestroyDescriptorSetLayout(device, m_lightUBODescriptorSetLayout,
nullptr);
1124 m_lightUBODescriptorSetLayout = VK_NULL_HANDLE;
1126 if (boneDescriptorSetLayout) {
1127 vkDestroyDescriptorSetLayout(device, boneDescriptorSetLayout,
nullptr);
1128 boneDescriptorSetLayout = VK_NULL_HANDLE;
1130 if (descriptorSetLayout) {
1131 vkDestroyDescriptorSetLayout(device, descriptorSetLayout,
nullptr);
1132 descriptorSetLayout = VK_NULL_HANDLE;
1136 CleanupDepthResources();
1139 if (swapChain && device) {
1140 vkDestroySwapchainKHR(device, swapChain,
nullptr);
1141 swapChain = VK_NULL_HANDLE;
1145 for (
auto& buffer : swapChainFramebuffers) {
1147 vkDestroyFramebuffer(device, buffer,
nullptr);
1148 buffer = VK_NULL_HANDLE;
1151 swapChainFramebuffers.clear();
1154 for (
auto& imgView : swapChainImageViews) {
1156 vkDestroyImageView(device, imgView,
nullptr);
1157 imgView = VK_NULL_HANDLE;
1160 swapChainImageViews.clear();
1163 for (
auto& sem : imageAvailableSemaphores) {
1164 if (sem) vkDestroySemaphore(device, sem,
nullptr);
1166 imageAvailableSemaphores.clear();
1168 for (
auto& sem : renderFinishedSemaphores) {
1169 if (sem) vkDestroySemaphore(device, sem,
nullptr);
1171 renderFinishedSemaphores.clear();
1173 for (
auto& fence : inFlightFences) {
1174 if (fence) vkDestroyFence(device, fence,
nullptr);
1176 inFlightFences.clear();
1179 for (uint32_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
1180 if (m_transferSemaphores[i]) {
1181 vkDestroySemaphore(device, m_transferSemaphores[i],
nullptr);
1182 m_transferSemaphores[i] = VK_NULL_HANDLE;
1184 auto& af = m_asyncFlush[i];
1185 if (af.commandBuffer != VK_NULL_HANDLE) {
1186 vkFreeCommandBuffers(af.device, af.commandPool, 1,
1189 for (
auto& pending : af.stagingBuffers) {
1199 delete simpleShader;
1200 simpleShader =
nullptr;
1205 vkDestroyPipeline(device, pipeline,
nullptr);
1206 pipeline = VK_NULL_HANDLE;
1211 vkDestroyPipelineLayout(device, pipelineLay,
nullptr);
1212 pipelineLay = VK_NULL_HANDLE;
1217 vkDestroyRenderPass(device, renderPass,
nullptr);
1218 renderPass = VK_NULL_HANDLE;
1222 if (m_defaultTexture) {
1223 delete m_defaultTexture;
1224 m_defaultTexture =
nullptr;
1229 vkDestroyCommandPool(device, commands,
nullptr);
1230 commands = VK_NULL_HANDLE;
1234 if (surface && instance) {
1235 vkDestroySurfaceKHR(instance, surface,
nullptr);
1236 surface = VK_NULL_HANDLE;
1240 if (debugMessenger && vkDestroyDebugUtilsMessengerEXT) {
1241 vkDestroyDebugUtilsMessengerEXT(instance, debugMessenger,
nullptr);
1242 debugMessenger = VK_NULL_HANDLE;
1252 vkDestroyDevice(device,
nullptr);
1253 device = VK_NULL_HANDLE;
1258 vkDestroyInstance(instance,
nullptr);
1259 instance = VK_NULL_HANDLE;
1266 RecreateSwapChain();
1272bool VulkanRenderer::RecreateSwapChain() {
1273 vkDeviceWaitIdle(device);
1277 bool hadGBuffer = m_gbufferResourcesCreated;
1278 CleanupGBufferResources();
1280 CleanupMSAAColorResources();
1283 if (!CreateSwapChain()) {
1287 if (!CreateImageViews()) {
1291 if (!CreateDepthResources()) {
1292 SLEAK_ERROR(
"Failed to recreate depth resources!");
1295 if (!CreateMSAAColorResources()) {
1296 SLEAK_ERROR(
"Failed to recreate MSAA color resources!");
1299 if (!CreateFrameBuffer()) {
1305 imagesInFlight.resize(swapChainImages.size(), VK_NULL_HANDLE);
1309 if (!CreateGBufferResources())
1310 SLEAK_WARN(
"RecreateSwapChain: Failed to recreate GBuffer resources!");
1328 case 1: m_msaaSamples = VK_SAMPLE_COUNT_1_BIT;
break;
1329 case 2: m_msaaSamples = VK_SAMPLE_COUNT_2_BIT;
break;
1330 case 4: m_msaaSamples = VK_SAMPLE_COUNT_4_BIT;
break;
1331 case 8: m_msaaSamples = VK_SAMPLE_COUNT_8_BIT;
break;
1332 default: m_msaaSamples = VK_SAMPLE_COUNT_1_BIT;
break;
1335 SLEAK_INFO(
"Applying MSAA change: {}x", newCount);
1337 vkDeviceWaitIdle(device);
1341 vkDestroyRenderPass(device, renderPass,
nullptr);
1342 renderPass = VK_NULL_HANDLE;
1347 vkDestroyPipeline(device, pipeline,
nullptr);
1348 pipeline = VK_NULL_HANDLE;
1350 if (skyboxPipeline) {
1351 vkDestroyPipeline(device, skyboxPipeline,
nullptr);
1352 skyboxPipeline = VK_NULL_HANDLE;
1354 if (skinnedPipeline) {
1355 vkDestroyPipeline(device, skinnedPipeline,
nullptr);
1356 skinnedPipeline = VK_NULL_HANDLE;
1358 if (debugLinePipeline) {
1359 vkDestroyPipeline(device, debugLinePipeline,
nullptr);
1360 debugLinePipeline = VK_NULL_HANDLE;
1364 DestroyCustomFormatPipelines();
1367 CleanupGBufferResources();
1371 ImGui_ImplVulkan_Shutdown();
1372 ImGui_ImplSDL3_Shutdown();
1373 ImGui::DestroyContext();
1378 CleanupMSAAColorResources();
1384 CreateDepthResources();
1385 CreateMSAAColorResources();
1387 CreateFrameBuffer();
1388 CreateGraphicsPipeline();
1389 CreateSkyboxPipeline();
1390 CreateSkinnedPipeline();
1391 CreateDebugLinePipeline();
1396 if (m_skyboxCubemapView != VK_NULL_HANDLE && m_skyboxCubemapSampler != VK_NULL_HANDLE) {
1397 for (
size_t i = 0; i < skyboxDescriptorSets.size(); i++) {
1398 VkDescriptorImageInfo imageInfo{};
1399 imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1400 imageInfo.imageView = m_skyboxCubemapView;
1401 imageInfo.sampler = m_skyboxCubemapSampler;
1403 VkWriteDescriptorSet descriptorWrite{};
1404 descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1405 descriptorWrite.dstSet = skyboxDescriptorSets[i];
1406 descriptorWrite.dstBinding = 0;
1407 descriptorWrite.dstArrayElement = 0;
1408 descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1409 descriptorWrite.descriptorCount = 1;
1410 descriptorWrite.pImageInfo = &imageInfo;
1412 vkUpdateDescriptorSets(device, 1, &descriptorWrite, 0,
nullptr);
1414 m_skyboxDescriptorsWritten =
true;
1417 SLEAK_INFO(
"MSAA change applied successfully");
1425 RecreateSwapChain();
1440bool VulkanRenderer::CreateCommandPool() {
1441 VkCommandPoolCreateInfo poolInfo{};
1442 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
1443 poolInfo.queueFamilyIndex = QueueIDs.GraphicsIndex;
1444 poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
1446 if (vkCreateCommandPool(device, &poolInfo,
nullptr, &commands) !=
1455bool VulkanRenderer::CreateSyncObjects() {
1456 uint32_t imageCount =
static_cast<uint32_t
>(swapChainImages.size());
1460 imageAvailableSemaphores.resize(imageCount);
1461 renderFinishedSemaphores.resize(imageCount);
1462 inFlightFences.resize(MAX_FRAMES_IN_FLIGHT);
1463 imagesInFlight.resize(imageCount, VK_NULL_HANDLE);
1465 VkSemaphoreCreateInfo semaphoreInfo{};
1466 semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
1468 VkFenceCreateInfo fenceInfo{};
1469 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
1470 fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
1473 for (uint32_t i = 0; i < imageCount; i++) {
1474 if (vkCreateSemaphore(device, &semaphoreInfo,
nullptr,
1475 &imageAvailableSemaphores[i]) != VK_SUCCESS ||
1476 vkCreateSemaphore(device, &semaphoreInfo,
nullptr,
1477 &renderFinishedSemaphores[i]) != VK_SUCCESS) {
1483 for (uint32_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
1484 if (vkCreateSemaphore(device, &semaphoreInfo,
nullptr,
1485 &m_transferSemaphores[i]) != VK_SUCCESS ||
1486 vkCreateFence(device, &fenceInfo,
nullptr,
1487 &inFlightFences[i]) != VK_SUCCESS) {
1490 m_asyncFlush[i] = {};
1493 m_semaphoreIndex = 0;
1506 vkDeviceWaitIdle(device);
1508 vkDestroyFramebuffer(device, m_shadowFramebuffer,
nullptr);
1509 m_shadowFramebuffer = VK_NULL_HANDLE;
1510 vkDestroyImageView(device, m_shadowImageView,
nullptr);
1511 m_shadowImageView = VK_NULL_HANDLE;
1512 vkDestroyImage(device, m_shadowImage,
nullptr);
1513 m_shadowImage = VK_NULL_HANDLE;
1514 vkFreeMemory(device, m_shadowImageMemory,
nullptr);
1515 m_shadowImageMemory = VK_NULL_HANDLE;
1519 VkImageCreateInfo imageInfo{};
1520 imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
1521 imageInfo.imageType = VK_IMAGE_TYPE_2D;
1523 imageInfo.mipLevels = 1;
1524 imageInfo.arrayLayers = 1;
1525 imageInfo.format = VK_FORMAT_D32_SFLOAT;
1526 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
1527 imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1528 imageInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT |
1529 VK_IMAGE_USAGE_SAMPLED_BIT;
1530 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
1531 imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
1533 if (vkCreateImage(device, &imageInfo,
nullptr, &m_shadowImage) !=
1535 SLEAK_ERROR(
"Shadow resolution change: image creation failed");
1540 VkMemoryRequirements memReqs;
1541 vkGetImageMemoryRequirements(device, m_shadowImage, &memReqs);
1543 VkMemoryAllocateInfo allocInfo{};
1544 allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
1545 allocInfo.allocationSize = memReqs.size;
1546 allocInfo.memoryTypeIndex = FindMemoryType(
1547 memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
1549 if (vkAllocateMemory(device, &allocInfo,
nullptr, &m_shadowImageMemory) !=
1551 SLEAK_ERROR(
"Shadow resolution change: memory allocation failed");
1555 vkBindImageMemory(device, m_shadowImage, m_shadowImageMemory, 0);
1557 VkImageViewCreateInfo viewInfo{};
1558 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
1559 viewInfo.image = m_shadowImage;
1560 viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
1561 viewInfo.format = VK_FORMAT_D32_SFLOAT;
1562 viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
1563 viewInfo.subresourceRange.baseMipLevel = 0;
1564 viewInfo.subresourceRange.levelCount = 1;
1565 viewInfo.subresourceRange.baseArrayLayer = 0;
1566 viewInfo.subresourceRange.layerCount = 1;
1568 if (vkCreateImageView(device, &viewInfo,
nullptr, &m_shadowImageView) !=
1570 SLEAK_ERROR(
"Shadow resolution change: image view creation failed");
1575 VkFramebufferCreateInfo fbInfo{};
1576 fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
1577 fbInfo.renderPass = m_shadowRenderPass;
1578 fbInfo.attachmentCount = 1;
1579 fbInfo.pAttachments = &m_shadowImageView;
1584 if (vkCreateFramebuffer(device, &fbInfo,
nullptr, &m_shadowFramebuffer) !=
1586 SLEAK_ERROR(
"Shadow resolution change: framebuffer creation failed");
1593 VkCommandBufferAllocateInfo cmdAllocInfo{};
1594 cmdAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1595 cmdAllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1596 cmdAllocInfo.commandPool = commands;
1597 cmdAllocInfo.commandBufferCount = 1;
1599 VkCommandBuffer cmdBuf;
1600 vkAllocateCommandBuffers(device, &cmdAllocInfo, &cmdBuf);
1602 VkCommandBufferBeginInfo cmdBeginInfo{};
1603 cmdBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
1604 cmdBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
1605 vkBeginCommandBuffer(cmdBuf, &cmdBeginInfo);
1607 VkImageMemoryBarrier barrier{};
1608 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
1609 barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1610 barrier.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
1611 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1612 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1613 barrier.image = m_shadowImage;
1614 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
1615 barrier.subresourceRange.baseMipLevel = 0;
1616 barrier.subresourceRange.levelCount = 1;
1617 barrier.subresourceRange.baseArrayLayer = 0;
1618 barrier.subresourceRange.layerCount = 1;
1619 barrier.srcAccessMask = 0;
1620 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
1622 vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
1623 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0,
1624 nullptr, 0,
nullptr, 1, &barrier);
1626 vkEndCommandBuffer(cmdBuf);
1628 VkSubmitInfo layoutSubmit{};
1629 layoutSubmit.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
1630 layoutSubmit.commandBufferCount = 1;
1631 layoutSubmit.pCommandBuffers = &cmdBuf;
1633 VkFenceCreateInfo layoutFenceInfo{};
1634 layoutFenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
1635 VkFence layoutFence;
1636 vkCreateFence(device, &layoutFenceInfo,
nullptr, &layoutFence);
1637 vkQueueSubmit(graphicsQueue, 1, &layoutSubmit, layoutFence);
1638 vkWaitForFences(device, 1, &layoutFence, VK_TRUE, UINT64_MAX);
1639 vkDestroyFence(device, layoutFence,
nullptr);
1640 vkFreeCommandBuffers(device, commands, 1, &cmdBuf);
1644 if (m_lightUBOCreated && m_shadowImageView && m_shadowSampler &&
1645 m_shadowRawSampler) {
1646 for (uint32_t i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) {
1647 VkDescriptorImageInfo compareInfo{};
1648 compareInfo.imageLayout =
1649 VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
1650 compareInfo.imageView = m_shadowImageView;
1651 compareInfo.sampler = m_shadowSampler;
1653 VkDescriptorImageInfo rawInfo{};
1654 rawInfo.imageLayout =
1655 VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
1656 rawInfo.imageView = m_shadowImageView;
1657 rawInfo.sampler = m_shadowRawSampler;
1659 std::array<VkWriteDescriptorSet, 2> writes{};
1660 writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1661 writes[0].dstSet = m_shadowSamplerDescriptorSets[i];
1662 writes[0].dstBinding = 0;
1663 writes[0].dstArrayElement = 0;
1664 writes[0].descriptorType =
1665 VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1666 writes[0].descriptorCount = 1;
1667 writes[0].pImageInfo = &compareInfo;
1669 writes[1] = writes[0];
1670 writes[1].dstBinding = 1;
1671 writes[1].pImageInfo = &rawInfo;
1673 vkUpdateDescriptorSets(device,
1674 static_cast<uint32_t
>(writes.size()),
1675 writes.data(), 0,
nullptr);
1679 SLEAK_INFO(
"VulkanRenderer: Shadow map resized to {}x{}",
1686bool VulkanRenderer::CreateGBufferResources() {
1687 if (m_gbufferResourcesCreated)
return true;
1690 for (uint32_t i = 0; i < GBUFFER_COUNT; ++i) {
1691 VkImageCreateInfo imageInfo{};
1692 imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
1693 imageInfo.imageType = VK_IMAGE_TYPE_2D;
1694 imageInfo.extent.width = scExtent.width;
1695 imageInfo.extent.height = scExtent.height;
1696 imageInfo.extent.depth = 1;
1697 imageInfo.mipLevels = 1;
1698 imageInfo.arrayLayers = 1;
1699 imageInfo.format = m_gbufferFormats[i];
1700 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
1701 imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1702 imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
1703 | VK_IMAGE_USAGE_SAMPLED_BIT;
1704 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
1705 imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
1707 if (vkCreateImage(device, &imageInfo,
nullptr, &m_gbufferImages[i]) != VK_SUCCESS) {
1708 SLEAK_ERROR(
"GBuffer: Failed to create GBuffer image {}!", i);
1712 VkMemoryRequirements memReqs;
1713 vkGetImageMemoryRequirements(device, m_gbufferImages[i], &memReqs);
1715 VkMemoryAllocateInfo allocInfo{};
1716 allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
1717 allocInfo.allocationSize = memReqs.size;
1718 allocInfo.memoryTypeIndex = FindMemoryType(memReqs.memoryTypeBits,
1719 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
1721 if (vkAllocateMemory(device, &allocInfo,
nullptr, &m_gbufferMemory[i]) != VK_SUCCESS) {
1722 SLEAK_ERROR(
"GBuffer: Failed to allocate GBuffer memory {}!", i);
1725 vkBindImageMemory(device, m_gbufferImages[i], m_gbufferMemory[i], 0);
1727 VkImageViewCreateInfo viewInfo{};
1728 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
1729 viewInfo.image = m_gbufferImages[i];
1730 viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
1731 viewInfo.format = m_gbufferFormats[i];
1732 viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1733 viewInfo.subresourceRange.baseMipLevel = 0;
1734 viewInfo.subresourceRange.levelCount = 1;
1735 viewInfo.subresourceRange.baseArrayLayer = 0;
1736 viewInfo.subresourceRange.layerCount = 1;
1738 if (vkCreateImageView(device, &viewInfo,
nullptr, &m_gbufferViews[i]) != VK_SUCCESS) {
1739 SLEAK_ERROR(
"GBuffer: Failed to create GBuffer image view {}!", i);
1744 if (!CreateGBufferRenderPass()) {
SLEAK_ERROR(
"GBuffer: render pass failed!");
return false; }
1745 if (!CreateGBufferFramebuffer()) {
SLEAK_ERROR(
"GBuffer: framebuffer failed!");
return false; }
1750 if (!CreateSSAOResources()) {
SLEAK_ERROR(
"GBuffer: SSAO resources failed!");
return false; }
1751 if (!CreateBloomResources()) {
SLEAK_ERROR(
"GBuffer: bloom/HDR resources failed!");
return false; }
1754 if (!CreateSSRResources()) {
SLEAK_ERROR(
"GBuffer: SSR resources failed!");
return false; }
1757 m_ssaoFallbackPrimed =
false;
1758 m_ssrFallbackPrimed =
false;
1759 m_bloomFallbackPrimed =
false;
1760 InitDisabledEffectFallbacks();
1761 if (!CreateGBufferDescriptorSets()) {
SLEAK_ERROR(
"GBuffer: descriptor sets failed!");
return false; }
1762 if (!CreateDeferredCBResources()) {
SLEAK_ERROR(
"GBuffer: deferred CB failed!");
return false; }
1763 if (!CreatePBRMaterialResources()) {
SLEAK_ERROR(
"GBuffer: PBR material resources failed!");
return false; }
1764 if (!CreateIBLResources()) {
SLEAK_ERROR(
"GBuffer: IBL resources failed!");
return false; }
1765 if (!CreateLightingRenderPass()) {
SLEAK_ERROR(
"GBuffer: lighting RP failed!");
return false; }
1766 if (!CreateLightingFramebuffers()) {
SLEAK_ERROR(
"GBuffer: lighting FBs failed!");
return false; }
1767 if (!CreateLightingPipeline()) {
SLEAK_ERROR(
"GBuffer: lighting pipeline failed!");
return false; }
1768 if (!CreateForwardRenderPass()) {
SLEAK_ERROR(
"GBuffer: forward RP failed!");
return false; }
1769 if (!CreateForwardFramebuffers()) {
SLEAK_ERROR(
"GBuffer: forward FBs failed!");
return false; }
1770 if (!CreateGBufferPipeline()) {
SLEAK_ERROR(
"GBuffer: gbuffer pipeline failed!");
return false; }
1771 if (!CreateSkinnedGbufferPipeline()) {
SLEAK_ERROR(
"GBuffer: skinned gbuffer pipeline failed!");
return false; }
1775 UpdateSSAODescriptors();
1778 UpdateSSRDescriptors();
1783 if (m_forwardRenderPass != VK_NULL_HANDLE) {
1784 if (pipeline) { vkDestroyPipeline(device, pipeline,
nullptr); pipeline = VK_NULL_HANDLE; }
1785 if (skyboxPipeline) { vkDestroyPipeline(device, skyboxPipeline,
nullptr); skyboxPipeline = VK_NULL_HANDLE; }
1786 if (debugLinePipeline) { vkDestroyPipeline(device, debugLinePipeline,
nullptr); debugLinePipeline = VK_NULL_HANDLE; }
1787 if (skinnedPipeline) { vkDestroyPipeline(device, skinnedPipeline,
nullptr); skinnedPipeline = VK_NULL_HANDLE; }
1788 if (m_skinnedGbufferPipeline) { vkDestroyPipeline(device, m_skinnedGbufferPipeline,
nullptr); m_skinnedGbufferPipeline = VK_NULL_HANDLE; }
1789 DestroyCustomFormatPipelines();
1791 if (skyboxDescriptorPool) {
1792 vkDestroyDescriptorPool(device, skyboxDescriptorPool,
nullptr);
1793 skyboxDescriptorPool = VK_NULL_HANDLE;
1795 skyboxDescriptorSets.clear();
1796 delete skyboxShader;
1797 skyboxShader =
nullptr;
1799 CreateGraphicsPipeline();
1800 CreateSkyboxPipeline();
1801 CreateDebugLinePipeline();
1802 CreateSkinnedPipeline();
1803 CreateSkinnedGbufferPipeline();
1805 m_gbufferResourcesCreated =
true;
1808 if (m_skyboxCubemapView != VK_NULL_HANDLE && m_skyboxCubemapSampler != VK_NULL_HANDLE) {
1809 for (
size_t i = 0; i < skyboxDescriptorSets.size(); i++) {
1810 VkDescriptorImageInfo imageInfo{};
1811 imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1812 imageInfo.imageView = m_skyboxCubemapView;
1813 imageInfo.sampler = m_skyboxCubemapSampler;
1815 VkWriteDescriptorSet descriptorWrite{};
1816 descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1817 descriptorWrite.dstSet = skyboxDescriptorSets[i];
1818 descriptorWrite.dstBinding = 0;
1819 descriptorWrite.dstArrayElement = 0;
1820 descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1821 descriptorWrite.descriptorCount = 1;
1822 descriptorWrite.pImageInfo = &imageInfo;
1824 vkUpdateDescriptorSets(device, 1, &descriptorWrite, 0,
nullptr);
1826 m_skyboxDescriptorsWritten =
true;
1830 SLEAK_INFO(
"VulkanRenderer: Deferred GBuffer resources created ({}x{})",
1831 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()
static AsyncFlushResult FlushPendingCopiesAsync(VkSemaphore signalSemaphore)
static VmaAllocator GetAllocator()
static VkDeviceSize GetDeviceLocalHeapSize()
static void SetBatchingEnabled(bool enabled)
Vulkan cubemap texture, loadable from six face images or a single equirectangular panorama.
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.
Vulkan vertex+fragment shader pair, loaded from precompiled SPIR-V modules.
Vulkan 2D texture: image + view + sampler, with per-swapchain-image descriptor sets.
const std::vector< VkDescriptorSet > & GetDescriptorSets() const
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