Program Listing for File VulkanRenderer.cpp#
↰ Return to documentation for file (Src/GraphicsEngineVulkan/renderer/VulkanRenderer.cpp)
module;
#include "common/Utilities.hpp"
#include "hostDevice/host_device_shared_vars.hpp"
#include "renderer/pushConstants/PushConstantPost.hpp"
#include "renderer/pushConstants/PushConstantRasterizer.hpp"
#include "renderer/pushConstants/PushConstantRayTracing.hpp"
#include "spdlog/spdlog.h"
#include <cstdint>
#include <glm/ext/matrix_clip_space.hpp>
#include <glm/trigonometric.hpp>
#include <limits>
#include <vulkan/vulkan.hpp>
#define GLFW_INCLUDE_NONE
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <array>
#include <cstring>
#include <memory>
#include <tuple>
#include <vector>
#ifndef VMA_IMPLEMENTATION
#define VMA_IMPLEMENTATION
#endif// !VMA_IMPLEMENTATION
#include <vk_mem_alloc.h>
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
#include <imgui.h>
#include <imgui_internal.h>
#include "common/Globals.hpp"
module kataglyphis.vulkan.renderer;
import kataglyphis.vulkan.device;
import kataglyphis.vulkan.gui_renderer_shared_vars;
import kataglyphis.vulkan.gui_scene_shared_vars;
import kataglyphis.vulkan.object_description;
import kataglyphis.vulkan.queue_family_indices;
import kataglyphis.vulkan.debug;
import kataglyphis.vulkan.scene;
import kataglyphis.vulkan.texture;
import kataglyphis.vulkan.as_manager;
import kataglyphis.vulkan.buffer_manager;
import kataglyphis.vulkan.buffer;
import kataglyphis.vulkan.camera;
import kataglyphis.vulkan.command_buffer_manager;
import kataglyphis.vulkan.instance;
import kataglyphis.vulkan.gui;
import kataglyphis.vulkan.scene_ubo;
import kataglyphis.vulkan.global_ubo;
import kataglyphis.vulkan.swapchain;
import kataglyphis.vulkan.allocator;
import kataglyphis.vulkan.window;
namespace {
[[maybe_unused]] vk::Result toVkResult(VkResult result) { return static_cast<vk::Result>(result); }
}// namespace
Kataglyphis::VulkanRenderer::VulkanRenderer(Kataglyphis::Frontend::Window *window,
Scene *scene,
Kataglyphis::Frontend::GUI *gui,
Camera *camera)
: window(window), scene(scene), gui(gui)
{
updateUniforms(scene, camera, window);
instance = VulkanInstance();
vk::DebugReportFlagsEXT const debugReportFlags =
vk::DebugReportFlagBitsEXT::eError | vk::DebugReportFlagBitsEXT::eWarning;
if (Kataglyphis::ENABLE_VALIDATION_LAYERS) {
debug::setupDebugging(instance.getVulkanInstance(), debugReportFlags, nullptr);
}
create_surface();
device = std::make_unique<VulkanDevice>(&instance, &surface);
allocator = Allocator(device->getLogicalDevice(), device->getPhysicalDevice(), instance.getVulkanInstance());
create_command_pool();
vulkanSwapChain.initVulkanContext(device.get(), window, surface);
create_uniform_buffers();
create_command_buffers();
createSynchronization();
createSharedRenderDescriptorSetLayouts();
std::vector<vk::DescriptorSetLayout> const descriptor_set_layouts_rasterizer = { sharedRenderDescriptorSetLayout };
rasterizer.init(device.get(), &vulkanSwapChain, descriptor_set_layouts_rasterizer, graphics_command_pool);
create_post_descriptor_layout();
std::vector<vk::DescriptorSetLayout> const descriptor_set_layouts_post = { post_descriptor_set_layout };
postStage.init(device.get(), &vulkanSwapChain, descriptor_set_layouts_post);
createDescriptorPoolSharedRenderStages();
createSharedRenderDescriptorSet();
updatePostDescriptorSets();
std::vector<vk::DescriptorSetLayout> layouts;
layouts.push_back(sharedRenderDescriptorSetLayout);
if (device->supportsHardwareAcceleratedRRT()) {
createRaytracingDescriptorPool();
createRaytracingDescriptorSetLayouts();
layouts.push_back(raytracingDescriptorSetLayout);
raytracingStage.init(device.get(), layouts);
pathTracing.init(device.get(), layouts);
}
scene->loadModel(device.get(), graphics_command_pool);
updateTexturesInSharedRenderDescriptorSet();
if (device->supportsHardwareAcceleratedRRT()) {
asManager.createASForScene(device.get(), graphics_command_pool, scene);
}
create_object_description_buffer();
if (device->supportsHardwareAcceleratedRRT()) {
createRaytracingDescriptorSets();
updateRaytracingDescriptorSets();
}
gui->initializeVulkanContext(device.get(),
instance.getVulkanInstance(),
postStage.getRenderPass(),
graphics_command_pool,
vulkanSwapChain.getNumberSwapChainImages());
gui->setUserSelectionForRRT(device->supportsHardwareAcceleratedRRT());
}
void Kataglyphis::VulkanRenderer::updateUniforms(Scene *scene_data,
Camera *camera_data,
Kataglyphis::Frontend::Window *window_data)
{
const GUISceneSharedVars guiSceneSharedVars = scene_data->getGuiSceneSharedVars();
globalUBO.view = camera_data->calculate_viewmatrix();
globalUBO.projection = glm::perspective(glm::radians(camera_data->get_fov()),
static_cast<float>(window_data->get_width()) / static_cast<float>(window_data->get_height()),
camera_data->get_near_plane(),
camera_data->get_far_plane());
sceneUBO.view_dir = glm::vec4(camera_data->get_camera_direction(), 1.0F);
sceneUBO.light_dir = glm::vec4(guiSceneSharedVars.directional_light_direction[0],
guiSceneSharedVars.directional_light_direction[1],
guiSceneSharedVars.directional_light_direction[2],
1.0F);
sceneUBO.cam_pos = glm::vec4(camera_data->get_camera_position(), camera_data->get_fov());
}
void Kataglyphis::VulkanRenderer::updateStateDueToUserInput(Kataglyphis::Frontend::GUI *frontend_gui)
{
Kataglyphis::VulkanRendererInternals::FrontendShared::GUIRendererSharedVars &guiRendererSharedVars =
frontend_gui->getGuiRendererSharedVars();
if (guiRendererSharedVars.shader_hot_reload_triggered) {
shaderHotReload();
guiRendererSharedVars.shader_hot_reload_triggered = false;
}
}
void Kataglyphis::VulkanRenderer::finishAllRenderCommands() { std::ignore = device->getLogicalDevice().waitIdle(); }
void Kataglyphis::VulkanRenderer::shaderHotReload()
{
std::ignore = device->getLogicalDevice().waitIdle();
std::vector<vk::DescriptorSetLayout> const descriptor_set_layouts = { sharedRenderDescriptorSetLayout };
rasterizer.shaderHotReload(descriptor_set_layouts);
std::vector<vk::DescriptorSetLayout> const descriptor_set_layouts_post = { post_descriptor_set_layout };
postStage.shaderHotReload(descriptor_set_layouts_post);
if (device->supportsHardwareAcceleratedRRT()) {
std::vector<vk::DescriptorSetLayout> const layouts = { sharedRenderDescriptorSetLayout,
raytracingDescriptorSetLayout };
raytracingStage.shaderHotReload(layouts);
pathTracing.shaderHotReload(layouts);
}
}
void Kataglyphis::VulkanRenderer::drawFrame()
{
const auto end_imgui_frame_if_needed = []() -> void {
ImGuiContext const *imgui_context = ImGui::GetCurrentContext();
if (imgui_context != nullptr && imgui_context->WithinFrameScope) { ImGui::EndFrame(); }
};
const auto abort_frame_with_fatal_error = [&](const char *message, vk::Result error_code) -> void {
spdlog::error(fmt::format("{} (vk::Result={})", message, static_cast<int>(error_code)));
if (error_code == vk::Result::eErrorDeviceLost) { device_lost_detected = true; }
if (window != nullptr && window->get_window() != nullptr) {
glfwSetWindowShouldClose(window->get_window(), GLFW_TRUE);
}
end_imgui_frame_if_needed();
};
if (frame_sync_count == 0) {
spdlog::error("No synchronization frames available; skipping draw frame.");
end_imgui_frame_if_needed();
return;
}
if (checkChangedFramebufferSize()) {
end_imgui_frame_if_needed();
return;
}
if (current_frame >= in_flight_fences.size() || current_frame >= image_available.size()) {
spdlog::error(fmt::format("Frame synchronization index out of range: {}", current_frame));
end_imgui_frame_if_needed();
return;
}
if (!in_flight_fences[current_frame] || !image_available[current_frame]) {
spdlog::error(fmt::format("Synchronization handles are invalid for frame {}.", current_frame));
if (window != nullptr && window->get_window() != nullptr) {
glfwSetWindowShouldClose(window->get_window(), GLFW_TRUE);
}
end_imgui_frame_if_needed();
return;
}
vk::Result result = device->getLogicalDevice().waitForFences(
1, &in_flight_fences[current_frame], VK_TRUE, std::numeric_limits<uint64_t>::max());
if (result != vk::Result::eSuccess) {
abort_frame_with_fatal_error("Failed to wait for fences!", result);
return;
}
uint32_t image_index = 0;
std::tie(result, image_index) = device->getLogicalDevice().acquireNextImageKHR(
vulkanSwapChain.getSwapChain(), std::numeric_limits<uint64_t>::max(), image_available[current_frame], nullptr);
if (result == vk::Result::eErrorOutOfDateKHR) {
end_imgui_frame_if_needed();
return;
}
if (result != vk::Result::eSuccess && result != vk::Result::eSuboptimalKHR) {
abort_frame_with_fatal_error("Failed to acquire next image!", result);
return;
}
if (image_index >= images_in_flight_fences.size() || image_index >= command_buffers.size()) {
spdlog::error(fmt::format("Swapchain image index out of range: {}", image_index));
end_imgui_frame_if_needed();
return;
}
if (image_index >= render_finished_by_image.size() || !render_finished_by_image[image_index]) {
spdlog::error(fmt::format("Render-finished semaphore missing for swapchain image {}.", image_index));
end_imgui_frame_if_needed();
return;
}
if (images_in_flight_fences[image_index]) {
result =
device->getLogicalDevice().waitForFences(1, &images_in_flight_fences[image_index], VK_TRUE, UINT64_MAX);
if (result != vk::Result::eSuccess) {
abort_frame_with_fatal_error("Failed to wait for image in-flight fence!", result);
return;
}
}
images_in_flight_fences[image_index] = in_flight_fences[current_frame];
result = command_buffers[image_index].reset(vk::CommandBufferResetFlags{});
if (result != vk::Result::eSuccess) {
abort_frame_with_fatal_error("Failed to reset command buffer!", result);
return;
}
vk::CommandBufferBeginInfo buffer_begin_info{};
buffer_begin_info.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;
result = command_buffers[image_index].begin(&buffer_begin_info);
if (result != vk::Result::eSuccess) {
abort_frame_with_fatal_error("Failed to start recording a command buffer!", result);
return;
}
update_uniform_buffers(image_index);
Kataglyphis::VulkanRendererInternals::FrontendShared::GUIRendererSharedVars const &guiRendererSharedVars =
gui->getGuiRendererSharedVars();
const bool raytracing_available = device->supportsHardwareAcceleratedRRT();
const char *const render_mode =
(!raytracing_available || (!guiRendererSharedVars.raytracing && !guiRendererSharedVars.pathTracing))
? "rasterizer"
: (guiRendererSharedVars.raytracing ? "raytracing" : "path_tracing");
if (raytracing_available && guiRendererSharedVars.raytracing) { update_raytracing_descriptor_set(image_index); }
if (!record_commands(image_index)) {
end_imgui_frame_if_needed();
return;
}
result = command_buffers[image_index].end();
if (result != vk::Result::eSuccess) {
abort_frame_with_fatal_error("Failed to stop recording a command buffer!", result);
return;
}
vk::SubmitInfo submit_info{};
submit_info.waitSemaphoreCount = 1;
submit_info.pWaitSemaphores = &image_available[current_frame];
vk::PipelineStageFlags const wait_stages = { vk::PipelineStageFlagBits::eColorAttachmentOutput };
submit_info.pWaitDstStageMask = &wait_stages;
submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &command_buffers[image_index];
submit_info.signalSemaphoreCount = 1;
submit_info.pSignalSemaphores = &render_finished_by_image[image_index];
result = device->getLogicalDevice().resetFences(1, &in_flight_fences[current_frame]);
if (result != vk::Result::eSuccess) {
abort_frame_with_fatal_error("Failed to reset fences!", result);
return;
}
result = device->getGraphicsQueue().submit(1, &submit_info, in_flight_fences[current_frame]);
if (result != vk::Result::eSuccess) {
spdlog::error(
fmt::format("Queue submit context: frame={}, imageIndex={}, renderMode={}, supportsRRT={}, cmdBufferIndex={}",
current_frame,
image_index,
render_mode,
raytracing_available,
image_index));
abort_frame_with_fatal_error("Failed to submit command buffer to queue!", result);
return;
}
vk::PresentInfoKHR present_info{};
present_info.waitSemaphoreCount = 1;
present_info.pWaitSemaphores = &render_finished_by_image[image_index];
present_info.swapchainCount = 1;
const vk::SwapchainKHR swapchain = vulkanSwapChain.getSwapChain();
present_info.pSwapchains = &swapchain;
present_info.pImageIndices = &image_index;
result = device->getPresentationQueue().presentKHR(&present_info);
if (result == vk::Result::eErrorOutOfDateKHR) {
end_imgui_frame_if_needed();
return;
}
if (result != vk::Result::eSuccess && result != vk::Result::eSuboptimalKHR) {
abort_frame_with_fatal_error("Failed to present image!", result);
return;
}
current_frame = (current_frame + 1) % frame_sync_count;
}
bool Kataglyphis::VulkanRenderer::checkChangedFramebufferSize()
{
if (window == nullptr) { return false; }
if (window->framebuffer_size_has_changed()) {
window->reset_framebuffer_has_changed();
return true;
}
return false;
}
void Kataglyphis::VulkanRenderer::update_uniform_buffers(uint32_t image_index)
{
if (image_index >= globalUBOBuffer.size() || image_index >= sceneUBOBuffer.size()) {
spdlog::error(fmt::format("Uniform buffer index out of range: {}", image_index));
return;
}
std::vector<VulkanRendererInternals::GlobalUBO> global_ubo_data;
global_ubo_data.push_back(globalUBO);
std::vector<VulkanRendererInternals::SceneUBO> scene_ubo_data;
scene_ubo_data.push_back(sceneUBO);
VulkanBuffer stagingGlobalUBO;
stagingGlobalUBO.create(device.get(),
sizeof(VulkanRendererInternals::GlobalUBO),
vk::BufferUsageFlagBits::eTransferSrc,
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent);
void *mapped_global_ubo =
device->getLogicalDevice()
.mapMemory(stagingGlobalUBO.getBufferMemory(), 0, sizeof(VulkanRendererInternals::GlobalUBO), {})
.value;
std::memcpy(mapped_global_ubo, global_ubo_data.data(), sizeof(VulkanRendererInternals::GlobalUBO));
device->getLogicalDevice().unmapMemory(stagingGlobalUBO.getBufferMemory());
auto const copy_buffer_ref = static_cast<void (Kataglyphis::VulkanBufferManager::*)(
vk::Device, vk::Queue, vk::CommandPool, VulkanBuffer &, VulkanBuffer &, vk::DeviceSize)>(
&Kataglyphis::VulkanBufferManager::copyBuffer);
(vulkanBufferManager.*copy_buffer_ref)(device->getLogicalDevice(),
device->getGraphicsQueue(),
graphics_command_pool,
stagingGlobalUBO,
globalUBOBuffer[image_index],
sizeof(VulkanRendererInternals::GlobalUBO));
stagingGlobalUBO.cleanUp();
VulkanBuffer stagingSceneUBO;
stagingSceneUBO.create(device.get(),
sizeof(VulkanRendererInternals::SceneUBO),
vk::BufferUsageFlagBits::eTransferSrc,
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent);
void *mapped_scene_ubo =
device->getLogicalDevice()
.mapMemory(stagingSceneUBO.getBufferMemory(), 0, sizeof(VulkanRendererInternals::SceneUBO), {})
.value;
std::memcpy(mapped_scene_ubo, scene_ubo_data.data(), sizeof(VulkanRendererInternals::SceneUBO));
device->getLogicalDevice().unmapMemory(stagingSceneUBO.getBufferMemory());
(vulkanBufferManager.*copy_buffer_ref)(device->getLogicalDevice(),
device->getGraphicsQueue(),
graphics_command_pool,
stagingSceneUBO,
sceneUBOBuffer[image_index],
sizeof(VulkanRendererInternals::SceneUBO));
stagingSceneUBO.cleanUp();
}
void Kataglyphis::VulkanRenderer::update_raytracing_descriptor_set(uint32_t image_index)
{
if (image_index >= raytracingDescriptorSet.size()) {
spdlog::error(fmt::format("Raytracing descriptor set index out of range: {}", image_index));
return;
}
vk::WriteDescriptorSetAccelerationStructureKHR descriptor_set_acceleration_structure{};
descriptor_set_acceleration_structure.accelerationStructureCount = 1;
vk::AccelerationStructureKHR &vulkanTLAS = asManager.getTLAS();
descriptor_set_acceleration_structure.pAccelerationStructures = &vulkanTLAS;
vk::WriteDescriptorSet write_descriptor_set_acceleration_structure{};
write_descriptor_set_acceleration_structure.pNext = &descriptor_set_acceleration_structure;
write_descriptor_set_acceleration_structure.dstSet = raytracingDescriptorSet[image_index];
write_descriptor_set_acceleration_structure.dstBinding = TLAS_BINDING;
write_descriptor_set_acceleration_structure.dstArrayElement = 0;
write_descriptor_set_acceleration_structure.descriptorCount = 1;
write_descriptor_set_acceleration_structure.descriptorType = vk::DescriptorType::eAccelerationStructureKHR;
vk::DescriptorImageInfo image_info{};
Texture &renderResult = rasterizer.getOffscreenTexture(image_index);
image_info.imageView = renderResult.getImageView();
image_info.imageLayout = vk::ImageLayout::eGeneral;
vk::WriteDescriptorSet descriptor_image_writer{};
descriptor_image_writer.dstSet = raytracingDescriptorSet[image_index];
descriptor_image_writer.dstBinding = OUT_IMAGE_BINDING;
descriptor_image_writer.dstArrayElement = 0;
descriptor_image_writer.descriptorCount = 1;
descriptor_image_writer.descriptorType = vk::DescriptorType::eStorageImage;
descriptor_image_writer.pImageInfo = &image_info;
std::vector<vk::WriteDescriptorSet> write_descriptor_sets = { write_descriptor_set_acceleration_structure,
descriptor_image_writer };
device->getLogicalDevice().updateDescriptorSets(write_descriptor_sets, {});
}
bool Kataglyphis::VulkanRenderer::record_commands(uint32_t image_index)
{
if (image_index >= command_buffers.size() || image_index >= sharedRenderDescriptorSet.size()
|| image_index >= post_descriptor_set.size()) {
spdlog::error(fmt::format("Command recording index out of range: {}", image_index));
return false;
}
vk::CommandBuffer &commandBuffer = command_buffers[image_index];
std::vector<vk::DescriptorSet> rasterizer_descriptor_sets = { sharedRenderDescriptorSet[image_index] };
rasterizer.recordCommands(commandBuffer, image_index, scene, rasterizer_descriptor_sets);
Kataglyphis::VulkanRendererInternals::FrontendShared::GUIRendererSharedVars const &guiRendererSharedVars =
gui->getGuiRendererSharedVars();
if (device->supportsHardwareAcceleratedRRT() && image_index < raytracingDescriptorSet.size()) {
std::vector<vk::DescriptorSet> raytracing_descriptor_sets = { sharedRenderDescriptorSet[image_index],
raytracingDescriptorSet[image_index] };
if (guiRendererSharedVars.raytracing) {
Texture &renderResult = rasterizer.getOffscreenTexture(image_index);
raytracingStage.recordCommands(
commandBuffer, renderResult.getVulkanImage(), &vulkanSwapChain, raytracing_descriptor_sets);
} else if (guiRendererSharedVars.pathTracing) {
Texture &renderResult = rasterizer.getOffscreenTexture(image_index);
pathTracing.recordCommands(
commandBuffer, image_index, renderResult.getVulkanImage(), &vulkanSwapChain, raytracing_descriptor_sets);
}
}
std::vector<vk::DescriptorSet> post_descriptor_sets = { post_descriptor_set[image_index] };
postStage.recordCommands(commandBuffer, image_index, post_descriptor_sets);
return true;
}
void Kataglyphis::VulkanRenderer::cleanUpUBOs()
{
for (VulkanBuffer &buffer : globalUBOBuffer) { buffer.cleanUp(); }
for (VulkanBuffer &buffer : sceneUBOBuffer) { buffer.cleanUp(); }
globalUBOBuffer.clear();
sceneUBOBuffer.clear();
}
void Kataglyphis::VulkanRenderer::cleanUp()
{
if (!device) { return; }
std::ignore = device->getLogicalDevice().waitIdle();
if (device->supportsHardwareAcceleratedRRT()) {
pathTracing.cleanUp();
raytracingStage.cleanUp();
asManager.cleanUp();
}
rasterizer.cleanUp();
postStage.cleanUp();
objectDescriptionBuffer.cleanUp();
cleanUpSync();
cleanUpUBOs();
cleanUpCommandPools();
if (post_descriptor_pool) {
device->getLogicalDevice().destroyDescriptorPool(post_descriptor_pool);
post_descriptor_pool = nullptr;
}
if (post_descriptor_set_layout) {
device->getLogicalDevice().destroyDescriptorSetLayout(post_descriptor_set_layout);
post_descriptor_set_layout = nullptr;
}
if (descriptorPoolSharedRenderStages) {
device->getLogicalDevice().destroyDescriptorPool(descriptorPoolSharedRenderStages);
descriptorPoolSharedRenderStages = nullptr;
}
if (sharedRenderDescriptorSetLayout) {
device->getLogicalDevice().destroyDescriptorSetLayout(sharedRenderDescriptorSetLayout);
sharedRenderDescriptorSetLayout = nullptr;
}
if (raytracingDescriptorPool) {
device->getLogicalDevice().destroyDescriptorPool(raytracingDescriptorPool);
raytracingDescriptorPool = nullptr;
}
if (raytracingDescriptorSetLayout) {
device->getLogicalDevice().destroyDescriptorSetLayout(raytracingDescriptorSetLayout);
raytracingDescriptorSetLayout = nullptr;
}
vulkanSwapChain.cleanUp();
allocator.cleanUp();
device->cleanUp();
device.reset();
if (surface) {
instance.getVulkanInstance().destroySurfaceKHR(surface);
surface = nullptr;
}
if (Kataglyphis::ENABLE_VALIDATION_LAYERS) { debug::freeDebugCallback(instance.getVulkanInstance()); }
instance.cleanUp();
}
Kataglyphis::VulkanRenderer::~VulkanRenderer() { cleanUp(); }
void Kataglyphis::VulkanRenderer::create_surface()
{
VkSurfaceKHR rawSurface = VK_NULL_HANDLE;
ASSERT_VULKAN(glfwCreateWindowSurface(instance.getVulkanInstance(), window->get_window(), nullptr, &rawSurface),
"Failed to create a surface!");
surface = vk::SurfaceKHR(rawSurface);
}
void Kataglyphis::VulkanRenderer::create_post_descriptor_layout()
{
vk::DescriptorSetLayoutBinding post_sampler_layout_binding{};
post_sampler_layout_binding.binding = 0;
post_sampler_layout_binding.descriptorType = vk::DescriptorType::eCombinedImageSampler;
post_sampler_layout_binding.descriptorCount = 1;
post_sampler_layout_binding.stageFlags = vk::ShaderStageFlagBits::eFragment;
post_sampler_layout_binding.pImmutableSamplers = nullptr;
std::vector<vk::DescriptorSetLayoutBinding> layout_bindings = { post_sampler_layout_binding };
vk::DescriptorSetLayoutCreateInfo layout_create_info{};
layout_create_info.bindingCount = static_cast<uint32_t>(layout_bindings.size());
layout_create_info.pBindings = layout_bindings.data();
vk::Result result =
device->getLogicalDevice().createDescriptorSetLayout(&layout_create_info, nullptr, &post_descriptor_set_layout);
ASSERT_VULKAN(static_cast<VkResult>(result), "Failed to create descriptor set layout!")
vk::DescriptorPoolSize post_pool_size{};
post_pool_size.type = vk::DescriptorType::eCombinedImageSampler;
post_pool_size.descriptorCount = vulkanSwapChain.getNumberSwapChainImages();
std::vector<vk::DescriptorPoolSize> descriptor_pool_sizes = { post_pool_size };
vk::DescriptorPoolCreateInfo pool_create_info{};
pool_create_info.maxSets = vulkanSwapChain.getNumberSwapChainImages();
pool_create_info.poolSizeCount = static_cast<uint32_t>(descriptor_pool_sizes.size());
pool_create_info.pPoolSizes = descriptor_pool_sizes.data();
result = device->getLogicalDevice().createDescriptorPool(&pool_create_info, nullptr, &post_descriptor_pool);
ASSERT_VULKAN(static_cast<VkResult>(result), "Failed to create a descriptor pool!")
post_descriptor_set.resize(vulkanSwapChain.getNumberSwapChainImages());
std::vector<vk::DescriptorSetLayout> set_layouts(
vulkanSwapChain.getNumberSwapChainImages(), post_descriptor_set_layout);
vk::DescriptorSetAllocateInfo set_alloc_info{};
set_alloc_info.descriptorPool = post_descriptor_pool;
set_alloc_info.descriptorSetCount = vulkanSwapChain.getNumberSwapChainImages();
set_alloc_info.pSetLayouts = set_layouts.data();
result = device->getLogicalDevice().allocateDescriptorSets(&set_alloc_info, post_descriptor_set.data());
ASSERT_VULKAN(static_cast<VkResult>(result), "Failed to create descriptor sets!")
if (result != vk::Result::eSuccess) {
post_descriptor_set.clear();
return;
}
}
void Kataglyphis::VulkanRenderer::updatePostDescriptorSets()
{
if (post_descriptor_set.size() < vulkanSwapChain.getNumberSwapChainImages()) {
spdlog::error("Post descriptor sets are not available; skipping update.");
return;
}
for (size_t i = 0; i < vulkanSwapChain.getNumberSwapChainImages(); i++) {
vk::DescriptorImageInfo image_info{};
image_info.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
Texture &renderResult = rasterizer.getOffscreenTexture(static_cast<uint32_t>(i));
image_info.imageView = renderResult.getImageView();
image_info.sampler = postStage.getOffscreenSampler();
vk::WriteDescriptorSet descriptor_write{};
descriptor_write.dstSet = post_descriptor_set[i];
descriptor_write.dstBinding = 0;
descriptor_write.dstArrayElement = 0;
descriptor_write.descriptorType = vk::DescriptorType::eCombinedImageSampler;
descriptor_write.descriptorCount = 1;
descriptor_write.pImageInfo = &image_info;
device->getLogicalDevice().updateDescriptorSets(1, &descriptor_write, 0, nullptr);
}
}
void Kataglyphis::VulkanRenderer::createRaytracingDescriptorPool()
{
std::array<vk::DescriptorPoolSize, 2> descriptor_pool_sizes{};
const uint32_t swapchain_image_count = vulkanSwapChain.getNumberSwapChainImages();
descriptor_pool_sizes[0].type = vk::DescriptorType::eAccelerationStructureKHR;
descriptor_pool_sizes[0].descriptorCount = swapchain_image_count;
descriptor_pool_sizes[1].type = vk::DescriptorType::eStorageImage;
descriptor_pool_sizes[1].descriptorCount = swapchain_image_count;
vk::DescriptorPoolCreateInfo descriptor_pool_create_info{};
descriptor_pool_create_info.poolSizeCount = static_cast<uint32_t>(descriptor_pool_sizes.size());
descriptor_pool_create_info.pPoolSizes = descriptor_pool_sizes.data();
descriptor_pool_create_info.maxSets = swapchain_image_count;
vk::Result const result =
device->getLogicalDevice().createDescriptorPool(&descriptor_pool_create_info, nullptr, &raytracingDescriptorPool);
ASSERT_VULKAN(static_cast<VkResult>(result), "Failed to create command pool!")
}
void Kataglyphis::VulkanRenderer::cleanUpSync()
{
for (vk::Semaphore semaphore : render_finished_by_image) {
if (semaphore) { device->getLogicalDevice().destroySemaphore(semaphore); }
}
for (uint32_t i = 0; i < frame_sync_count; i++) {
if (image_available[i]) { device->getLogicalDevice().destroySemaphore(image_available[i]); }
if (in_flight_fences[i]) { device->getLogicalDevice().destroyFence(in_flight_fences[i]); }
}
}
void Kataglyphis::VulkanRenderer::create_object_description_buffer()
{
std::vector<ObjectDescription> objectDescriptions = scene->getObjectDescriptions();
vulkanBufferManager.createBufferAndUploadVectorOnDevice(device.get(),
graphics_command_pool,
objectDescriptionBuffer,
vk::BufferUsageFlagBits::eTransferDst | vk::BufferUsageFlagBits::eStorageBuffer,
vk::MemoryPropertyFlagBits::eDeviceLocal,
objectDescriptions);
for (size_t i = 0; i < vulkanSwapChain.getNumberSwapChainImages(); i++) {
vk::DescriptorBufferInfo object_descriptions_buffer_info{};
object_descriptions_buffer_info.buffer = objectDescriptionBuffer.getBuffer();
object_descriptions_buffer_info.offset = 0;
object_descriptions_buffer_info.range = VK_WHOLE_SIZE;
vk::WriteDescriptorSet descriptor_object_descriptions_writer{};
descriptor_object_descriptions_writer.pNext = nullptr;
descriptor_object_descriptions_writer.dstSet = sharedRenderDescriptorSet[i];
descriptor_object_descriptions_writer.dstBinding = OBJECT_DESCRIPTION_BINDING;
descriptor_object_descriptions_writer.dstArrayElement = 0;
descriptor_object_descriptions_writer.descriptorCount = 1;
descriptor_object_descriptions_writer.descriptorType = vk::DescriptorType::eStorageBuffer;
descriptor_object_descriptions_writer.pImageInfo = nullptr;
descriptor_object_descriptions_writer.pBufferInfo = &object_descriptions_buffer_info;
descriptor_object_descriptions_writer.pTexelBufferView = nullptr;
std::vector<vk::WriteDescriptorSet> write_descriptor_sets = { descriptor_object_descriptions_writer };
device->getLogicalDevice().updateDescriptorSets(
static_cast<uint32_t>(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, nullptr);
}
}
void Kataglyphis::VulkanRenderer::createRaytracingDescriptorSetLayouts()
{
{
std::array<vk::DescriptorSetLayoutBinding, 2> descriptor_set_layout_bindings{};
descriptor_set_layout_bindings[0].binding = TLAS_BINDING;
descriptor_set_layout_bindings[0].descriptorCount = 1;
descriptor_set_layout_bindings[0].descriptorType = vk::DescriptorType::eAccelerationStructureKHR;
descriptor_set_layout_bindings[0].pImmutableSamplers = nullptr;
descriptor_set_layout_bindings[0].stageFlags = vk::ShaderStageFlagBits::eRaygenKHR
| vk::ShaderStageFlagBits::eClosestHitKHR
| vk::ShaderStageFlagBits::eCompute;
descriptor_set_layout_bindings[1].binding = OUT_IMAGE_BINDING;
descriptor_set_layout_bindings[1].descriptorCount = 1;
descriptor_set_layout_bindings[1].descriptorType = vk::DescriptorType::eStorageImage;
descriptor_set_layout_bindings[1].pImmutableSamplers = nullptr;
descriptor_set_layout_bindings[1].stageFlags = vk::ShaderStageFlagBits::eRaygenKHR
| vk::ShaderStageFlagBits::eClosestHitKHR
| vk::ShaderStageFlagBits::eCompute;
vk::DescriptorSetLayoutCreateInfo descriptor_set_layout_create_info{};
descriptor_set_layout_create_info.bindingCount = static_cast<uint32_t>(descriptor_set_layout_bindings.size());
descriptor_set_layout_create_info.pBindings = descriptor_set_layout_bindings.data();
vk::Result const result = device->getLogicalDevice().createDescriptorSetLayout(
&descriptor_set_layout_create_info, nullptr, &raytracingDescriptorSetLayout);
ASSERT_VULKAN(static_cast<VkResult>(result), "Failed to create raytracing descriptor set layout!")
}
}
void Kataglyphis::VulkanRenderer::createRaytracingDescriptorSets()
{
raytracingDescriptorSet.resize(vulkanSwapChain.getNumberSwapChainImages());
std::vector<vk::DescriptorSetLayout> set_layouts(
vulkanSwapChain.getNumberSwapChainImages(), raytracingDescriptorSetLayout);
vk::DescriptorSetAllocateInfo descriptor_set_allocate_info{};
descriptor_set_allocate_info.descriptorPool = raytracingDescriptorPool;
descriptor_set_allocate_info.descriptorSetCount = vulkanSwapChain.getNumberSwapChainImages();
descriptor_set_allocate_info.pSetLayouts = set_layouts.data();
vk::Result const result =
device->getLogicalDevice().allocateDescriptorSets(&descriptor_set_allocate_info, raytracingDescriptorSet.data());
ASSERT_VULKAN(static_cast<VkResult>(result), "Failed to allocate raytracing descriptor set!")
}
void Kataglyphis::VulkanRenderer::updateRaytracingDescriptorSets()
{
for (size_t i = 0; i < vulkanSwapChain.getNumberSwapChainImages(); i++) {
vk::WriteDescriptorSetAccelerationStructureKHR descriptor_set_acceleration_structure{};
descriptor_set_acceleration_structure.pNext = nullptr;
descriptor_set_acceleration_structure.accelerationStructureCount = 1;
vk::AccelerationStructureKHR &vulkanTLAS = asManager.getTLAS();
descriptor_set_acceleration_structure.pAccelerationStructures = &vulkanTLAS;
vk::WriteDescriptorSet write_descriptor_set_acceleration_structure{};
write_descriptor_set_acceleration_structure.pNext = &descriptor_set_acceleration_structure;
write_descriptor_set_acceleration_structure.dstSet = raytracingDescriptorSet[i];
write_descriptor_set_acceleration_structure.dstBinding = TLAS_BINDING;
write_descriptor_set_acceleration_structure.dstArrayElement = 0;
write_descriptor_set_acceleration_structure.descriptorCount = 1;
write_descriptor_set_acceleration_structure.descriptorType = vk::DescriptorType::eAccelerationStructureKHR;
write_descriptor_set_acceleration_structure.pImageInfo = nullptr;
write_descriptor_set_acceleration_structure.pBufferInfo = nullptr;
write_descriptor_set_acceleration_structure.pTexelBufferView = nullptr;
vk::DescriptorImageInfo image_info{};
Texture &renderResult = rasterizer.getOffscreenTexture(static_cast<uint32_t>(i));
image_info.imageView = renderResult.getImageView();
image_info.imageLayout = vk::ImageLayout::eGeneral;
vk::WriteDescriptorSet descriptor_image_writer{};
descriptor_image_writer.pNext = nullptr;
descriptor_image_writer.dstSet = raytracingDescriptorSet[i];
descriptor_image_writer.dstBinding = OUT_IMAGE_BINDING;
descriptor_image_writer.dstArrayElement = 0;
descriptor_image_writer.descriptorCount = 1;
descriptor_image_writer.descriptorType = vk::DescriptorType::eStorageImage;
descriptor_image_writer.pImageInfo = &image_info;
descriptor_image_writer.pBufferInfo = nullptr;
descriptor_image_writer.pTexelBufferView = nullptr;
std::vector<vk::WriteDescriptorSet> write_descriptor_sets = { write_descriptor_set_acceleration_structure,
descriptor_image_writer };
device->getLogicalDevice().updateDescriptorSets(
static_cast<uint32_t>(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, nullptr);
}
}
void Kataglyphis::VulkanRenderer::createSharedRenderDescriptorSetLayouts()
{
const bool raytracing_available = device->supportsHardwareAcceleratedRRT();
vk::ShaderStageFlags global_ubo_stages = vk::ShaderStageFlagBits::eVertex;
vk::ShaderStageFlags scene_ubo_stages = vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment;
vk::ShaderStageFlags object_description_stages =
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment;
vk::ShaderStageFlags sampler_stages = vk::ShaderStageFlagBits::eFragment;
vk::ShaderStageFlags textures_stages = vk::ShaderStageFlagBits::eFragment;
if (raytracing_available) {
global_ubo_stages |= vk::ShaderStageFlagBits::eRaygenKHR | vk::ShaderStageFlagBits::eCompute;
scene_ubo_stages |= vk::ShaderStageFlagBits::eRaygenKHR | vk::ShaderStageFlagBits::eClosestHitKHR
| vk::ShaderStageFlagBits::eCompute;
object_description_stages |= vk::ShaderStageFlagBits::eClosestHitKHR | vk::ShaderStageFlagBits::eCompute;
sampler_stages |= vk::ShaderStageFlagBits::eClosestHitKHR | vk::ShaderStageFlagBits::eCompute;
textures_stages |= vk::ShaderStageFlagBits::eClosestHitKHR | vk::ShaderStageFlagBits::eCompute;
}
std::array<vk::DescriptorSetLayoutBinding, 5> descriptor_set_layout_bindings{};
descriptor_set_layout_bindings[0].binding = globalUBO_BINDING;
descriptor_set_layout_bindings[0].descriptorType = vk::DescriptorType::eUniformBuffer;
descriptor_set_layout_bindings[0].descriptorCount = 1;
descriptor_set_layout_bindings[0].stageFlags = global_ubo_stages;
descriptor_set_layout_bindings[0].pImmutableSamplers = nullptr;
descriptor_set_layout_bindings[1].binding = sceneUBO_BINDING;
descriptor_set_layout_bindings[1].descriptorType = vk::DescriptorType::eUniformBuffer;
descriptor_set_layout_bindings[1].descriptorCount = 1;
descriptor_set_layout_bindings[1].stageFlags = scene_ubo_stages;
descriptor_set_layout_bindings[1].pImmutableSamplers = nullptr;
descriptor_set_layout_bindings[2].binding = OBJECT_DESCRIPTION_BINDING;
descriptor_set_layout_bindings[2].descriptorCount = 1;
descriptor_set_layout_bindings[2].descriptorType = vk::DescriptorType::eStorageBuffer;
descriptor_set_layout_bindings[2].pImmutableSamplers = nullptr;
descriptor_set_layout_bindings[2].stageFlags = object_description_stages;
descriptor_set_layout_bindings[3].binding = TEXTURES_BINDING;
descriptor_set_layout_bindings[3].descriptorType = vk::DescriptorType::eSampledImage;
descriptor_set_layout_bindings[3].descriptorCount = MAX_TEXTURE_COUNT;
descriptor_set_layout_bindings[3].stageFlags = textures_stages;
descriptor_set_layout_bindings[3].pImmutableSamplers = nullptr;
descriptor_set_layout_bindings[4].binding = SAMPLER_BINDING;
descriptor_set_layout_bindings[4].descriptorType = vk::DescriptorType::eSampler;
descriptor_set_layout_bindings[4].descriptorCount = MAX_TEXTURE_COUNT;
descriptor_set_layout_bindings[4].stageFlags = sampler_stages;
descriptor_set_layout_bindings[4].pImmutableSamplers = nullptr;
vk::DescriptorSetLayoutCreateInfo layout_create_info{};
layout_create_info.bindingCount = static_cast<uint32_t>(descriptor_set_layout_bindings.size());
layout_create_info.pBindings = descriptor_set_layout_bindings.data();
vk::Result const result = device->getLogicalDevice().createDescriptorSetLayout(
&layout_create_info, nullptr, &sharedRenderDescriptorSetLayout);
ASSERT_VULKAN(static_cast<VkResult>(result), "Failed to create descriptor set layout!")
}
void Kataglyphis::VulkanRenderer::create_command_pool()
{
Kataglyphis::VulkanRendererInternals::QueueFamilyIndices const queue_family_indices = device->getQueueFamilies();
{
vk::CommandPoolCreateInfo pool_info{};
pool_info.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer;
pool_info.queueFamilyIndex = static_cast<uint32_t>(queue_family_indices.graphics_family);
vk::Result const result =
device->getLogicalDevice().createCommandPool(&pool_info, nullptr, &graphics_command_pool);
ASSERT_VULKAN(static_cast<VkResult>(result), "Failed to create command pool!")
}
{
vk::CommandPoolCreateInfo pool_info{};
pool_info.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer;
pool_info.queueFamilyIndex = static_cast<uint32_t>(queue_family_indices.compute_family);
vk::Result const result =
device->getLogicalDevice().createCommandPool(&pool_info, nullptr, &compute_command_pool);
ASSERT_VULKAN(static_cast<VkResult>(result), "Failed to create command pool!")
}
}
void Kataglyphis::VulkanRenderer::cleanUpCommandPools()
{
if (graphics_command_pool) {
device->getLogicalDevice().destroyCommandPool(graphics_command_pool);
graphics_command_pool = nullptr;
}
if (compute_command_pool) {
device->getLogicalDevice().destroyCommandPool(compute_command_pool);
compute_command_pool = nullptr;
}
}
void Kataglyphis::VulkanRenderer::create_command_buffers()
{
command_buffers.resize(vulkanSwapChain.getNumberSwapChainImages());
vk::CommandBufferAllocateInfo command_buffer_alloc_info{};
command_buffer_alloc_info.commandPool = graphics_command_pool;
command_buffer_alloc_info.level = vk::CommandBufferLevel::ePrimary;
command_buffer_alloc_info.commandBufferCount = static_cast<uint32_t>(command_buffers.size());
vk::Result const result =
device->getLogicalDevice().allocateCommandBuffers(&command_buffer_alloc_info, command_buffers.data());
ASSERT_VULKAN(static_cast<VkResult>(result), "Failed to allocate command buffers!")
}
void Kataglyphis::VulkanRenderer::createSynchronization()
{
frame_sync_count = std::min<uint32_t>(
static_cast<uint32_t>(Kataglyphis::MAX_FRAME_DRAWS), vulkanSwapChain.getNumberSwapChainImages());
image_available.resize(frame_sync_count);
render_finished_by_image.resize(vulkanSwapChain.getNumberSwapChainImages());
in_flight_fences.resize(frame_sync_count);
images_in_flight_fences.resize(vulkanSwapChain.getNumberSwapChainImages());
vk::SemaphoreCreateInfo semaphore_create_info{};
vk::FenceCreateInfo fence_create_info{};
fence_create_info.flags = vk::FenceCreateFlagBits::eSignaled;
for (uint32_t i = 0; i < frame_sync_count; i++) {
auto image_available_result_value = device->getLogicalDevice().createSemaphore(semaphore_create_info);
auto image_available_result = image_available_result_value.result;
auto image_available_handle = image_available_result_value.value;
auto in_flight_fence_result_value = device->getLogicalDevice().createFence(fence_create_info);
auto in_flight_fence_result = in_flight_fence_result_value.result;
auto in_flight_fence_handle = in_flight_fence_result_value.value;
if (image_available_result != vk::Result::eSuccess || in_flight_fence_result != vk::Result::eSuccess
|| !image_available_handle || !in_flight_fence_handle) {
spdlog::error(
fmt::format("Failed to create synchronization objects for frame {} (imageAvailable={}, fence={}).",
i,
static_cast<int>(image_available_result),
static_cast<int>(in_flight_fence_result)));
frame_sync_count = 0;
return;
}
image_available[i] = image_available_handle;
in_flight_fences[i] = in_flight_fence_handle;
}
for (uint32_t image = 0; image < vulkanSwapChain.getNumberSwapChainImages(); ++image) {
auto render_finished_result_value = device->getLogicalDevice().createSemaphore(semaphore_create_info);
auto render_finished_result = render_finished_result_value.result;
auto render_finished_handle = render_finished_result_value.value;
if (render_finished_result != vk::Result::eSuccess || !render_finished_handle) {
spdlog::error(fmt::format("Failed to create render-finished semaphore for swapchain image {} ({}).",
image,
static_cast<int>(render_finished_result)));
frame_sync_count = 0;
return;
}
render_finished_by_image[image] = render_finished_handle;
}
}
void Kataglyphis::VulkanRenderer::create_uniform_buffers()
{
globalUBOBuffer.resize(vulkanSwapChain.getNumberSwapChainImages());
sceneUBOBuffer.resize(vulkanSwapChain.getNumberSwapChainImages());
std::vector<VulkanRendererInternals::GlobalUBO> globalUBOdata;
globalUBOdata.push_back(globalUBO);
std::vector<VulkanRendererInternals::SceneUBO> sceneUBOdata;
sceneUBOdata.push_back(sceneUBO);
for (size_t i = 0; i < vulkanSwapChain.getNumberSwapChainImages(); i++) {
vulkanBufferManager.createBufferAndUploadVectorOnDevice(device.get(),
graphics_command_pool,
globalUBOBuffer[i],
vk::BufferUsageFlagBits::eUniformBuffer | vk::BufferUsageFlagBits::eTransferDst,
vk::MemoryPropertyFlagBits::eDeviceLocal,
globalUBOdata);
vulkanBufferManager.createBufferAndUploadVectorOnDevice(device.get(),
graphics_command_pool,
sceneUBOBuffer[i],
vk::BufferUsageFlagBits::eUniformBuffer | vk::BufferUsageFlagBits::eTransferDst,
vk::MemoryPropertyFlagBits::eDeviceLocal,
sceneUBOdata);
}
}
void Kataglyphis::VulkanRenderer::createDescriptorPoolSharedRenderStages()
{
vk::DescriptorPoolSize vp_pool_size{};
vp_pool_size.type = vk::DescriptorType::eUniformBuffer;
vp_pool_size.descriptorCount = static_cast<uint32_t>(globalUBOBuffer.size());
vk::DescriptorPoolSize directions_pool_size{};
directions_pool_size.type = vk::DescriptorType::eUniformBuffer;
directions_pool_size.descriptorCount = static_cast<uint32_t>(sceneUBOBuffer.size());
vk::DescriptorPoolSize object_descriptions_pool_size{};
object_descriptions_pool_size.type = vk::DescriptorType::eStorageBuffer;
object_descriptions_pool_size.descriptorCount =
static_cast<uint32_t>(sizeof(ObjectDescription) * Kataglyphis::MAX_OBJECTS);
vk::DescriptorPoolSize sampler_pool_size{};
sampler_pool_size.type = vk::DescriptorType::eSampler;
sampler_pool_size.descriptorCount = MAX_TEXTURE_COUNT * vulkanSwapChain.getNumberSwapChainImages();
vk::DescriptorPoolSize sampled_image_pool_size{};
sampled_image_pool_size.type = vk::DescriptorType::eSampledImage;
sampled_image_pool_size.descriptorCount = MAX_TEXTURE_COUNT * vulkanSwapChain.getNumberSwapChainImages();
std::vector<vk::DescriptorPoolSize> descriptor_pool_sizes = {
vp_pool_size, directions_pool_size, object_descriptions_pool_size, sampler_pool_size, sampled_image_pool_size
};
vk::DescriptorPoolCreateInfo pool_create_info{};
pool_create_info.maxSets = vulkanSwapChain.getNumberSwapChainImages();
pool_create_info.poolSizeCount = static_cast<uint32_t>(descriptor_pool_sizes.size());
pool_create_info.pPoolSizes = descriptor_pool_sizes.data();
vk::Result const result =
device->getLogicalDevice().createDescriptorPool(&pool_create_info, nullptr, &descriptorPoolSharedRenderStages);
ASSERT_VULKAN(static_cast<VkResult>(result), "Failed to create a descriptor pool!")
}
void Kataglyphis::VulkanRenderer::createSharedRenderDescriptorSet()
{
sharedRenderDescriptorSet.resize(vulkanSwapChain.getNumberSwapChainImages());
std::vector<vk::DescriptorSetLayout> set_layouts(
vulkanSwapChain.getNumberSwapChainImages(), sharedRenderDescriptorSetLayout);
vk::DescriptorSetAllocateInfo set_alloc_info{};
set_alloc_info.descriptorPool = descriptorPoolSharedRenderStages;
set_alloc_info.descriptorSetCount = vulkanSwapChain.getNumberSwapChainImages();
set_alloc_info.pSetLayouts = set_layouts.data();
vk::Result const result =
device->getLogicalDevice().allocateDescriptorSets(&set_alloc_info, sharedRenderDescriptorSet.data());
ASSERT_VULKAN(static_cast<VkResult>(result), "Failed to create descriptor sets!")
if (result != vk::Result::eSuccess) {
sharedRenderDescriptorSet.clear();
return;
}
for (size_t i = 0; i < vulkanSwapChain.getNumberSwapChainImages(); i++) {
vk::DescriptorBufferInfo globalUBO_buffer_info{};
globalUBO_buffer_info.buffer = globalUBOBuffer[i].getBuffer();
globalUBO_buffer_info.offset = 0;
globalUBO_buffer_info.range = sizeof(globalUBO);
vk::WriteDescriptorSet globalUBO_set_write{};
globalUBO_set_write.dstSet = sharedRenderDescriptorSet[i];
globalUBO_set_write.dstBinding = 0;
globalUBO_set_write.dstArrayElement = 0;
globalUBO_set_write.descriptorType = vk::DescriptorType::eUniformBuffer;
globalUBO_set_write.descriptorCount = 1;
globalUBO_set_write.pBufferInfo = &globalUBO_buffer_info;
vk::DescriptorBufferInfo sceneUBO_buffer_info{};
sceneUBO_buffer_info.buffer = sceneUBOBuffer[i].getBuffer();
sceneUBO_buffer_info.offset = 0;
sceneUBO_buffer_info.range = sizeof(sceneUBO);
vk::WriteDescriptorSet sceneUBO_set_write{};
sceneUBO_set_write.dstSet = sharedRenderDescriptorSet[i];
sceneUBO_set_write.dstBinding = 1;
sceneUBO_set_write.dstArrayElement = 0;
sceneUBO_set_write.descriptorType = vk::DescriptorType::eUniformBuffer;
sceneUBO_set_write.descriptorCount = 1;
sceneUBO_set_write.pBufferInfo = &sceneUBO_buffer_info;
std::vector<vk::WriteDescriptorSet> write_descriptor_sets = { globalUBO_set_write, sceneUBO_set_write };
device->getLogicalDevice().updateDescriptorSets(
static_cast<uint32_t>(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, nullptr);
}
}
void Kataglyphis::VulkanRenderer::updateTexturesInSharedRenderDescriptorSet()
{
if (sharedRenderDescriptorSet.size() < vulkanSwapChain.getNumberSwapChainImages()) {
spdlog::error("Shared render descriptor sets are not available; skipping texture update.");
return;
}
std::vector<Texture> &modelTextures = scene->getTextures(0);
const uint32_t scene_texture_count = scene->getTextureCount(0);
const uint32_t texture_count_for_descriptors = std::min<uint32_t>(scene_texture_count, MAX_TEXTURE_COUNT);
if (scene_texture_count > MAX_TEXTURE_COUNT) {
spdlog::warn(fmt::format("Scene has {} textures, but MAX_TEXTURE_COUNT is {}. Clamping descriptor updates.",
scene_texture_count,
MAX_TEXTURE_COUNT));
}
if (texture_count_for_descriptors == 0) {
spdlog::error("No textures available for descriptor update.");
return;
}
std::vector<vk::DescriptorImageInfo> image_info_textures;
image_info_textures.resize(MAX_TEXTURE_COUNT);
for (uint32_t i = 0; i < texture_count_for_descriptors; i++) {
image_info_textures[i].imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
image_info_textures[i].imageView = modelTextures[i].getImageView();
image_info_textures[i].sampler = nullptr;
}
for (uint32_t i = texture_count_for_descriptors; i < MAX_TEXTURE_COUNT; i++) {
image_info_textures[i].imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
image_info_textures[i].imageView = modelTextures[0].getImageView();
image_info_textures[i].sampler = nullptr;
}
std::vector<vk::Sampler> &modelTextureSampler = scene->getTextureSampler(0);
std::vector<vk::DescriptorImageInfo> image_info_texture_sampler;
image_info_texture_sampler.resize(MAX_TEXTURE_COUNT);
for (uint32_t i = 0; i < texture_count_for_descriptors; i++) {
image_info_texture_sampler[i].imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
image_info_texture_sampler[i].imageView = nullptr;
image_info_texture_sampler[i].sampler = modelTextureSampler[i];
}
for (uint32_t i = texture_count_for_descriptors; i < MAX_TEXTURE_COUNT; i++) {
image_info_texture_sampler[i].imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
image_info_texture_sampler[i].imageView = nullptr;
image_info_texture_sampler[i].sampler = modelTextureSampler[0];
}
for (uint32_t i = 0; i < vulkanSwapChain.getNumberSwapChainImages(); i++) {
vk::WriteDescriptorSet descriptor_write{};
descriptor_write.dstSet = sharedRenderDescriptorSet[i];
descriptor_write.dstBinding = TEXTURES_BINDING;
descriptor_write.dstArrayElement = 0;
descriptor_write.descriptorType = vk::DescriptorType::eSampledImage;
descriptor_write.descriptorCount = MAX_TEXTURE_COUNT;
descriptor_write.pImageInfo = image_info_textures.data();
vk::WriteDescriptorSet descriptor_sampler_write{};
descriptor_sampler_write.dstSet = sharedRenderDescriptorSet[i];
descriptor_sampler_write.dstBinding = SAMPLER_BINDING;
descriptor_sampler_write.dstArrayElement = 0;
descriptor_sampler_write.descriptorType = vk::DescriptorType::eSampler;
descriptor_sampler_write.descriptorCount = MAX_TEXTURE_COUNT;
descriptor_sampler_write.pImageInfo = image_info_texture_sampler.data();
std::vector<vk::WriteDescriptorSet> write_descriptor_sets = { descriptor_write, descriptor_sampler_write };
device->getLogicalDevice().updateDescriptorSets(
static_cast<uint32_t>(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, nullptr);
}
}