Program Listing for File DeferredRasterizer.cpp#
↰ Return to documentation for file (Src/GraphicsEngineVulkan/renderer/DeferredRasterizer.cpp)
module;
#include <memory>
#include <array>
#include <cstddef>
#include <cstdint>
#include <filesystem>
#include <vector>
#include <vulkan/vulkan.hpp>
#include "renderer/pushConstants/PushConstantRasterizer.hpp"
#include "common/FormatHelper.hpp"
#include "common/Utilities.hpp"
module kataglyphis.vulkan.deferred_rasterizer;
import kataglyphis.vulkan.file;
import kataglyphis.vulkan.vertex;
import kataglyphis.vulkan.texture;
import kataglyphis.vulkan.image;
import kataglyphis.vulkan.scene;
import kataglyphis.vulkan.shader_helper;
using namespace Kataglyphis::VulkanRendererInternals;
DeferredRasterizer::DeferredRasterizer() = default;
void DeferredRasterizer::init(std::shared_ptr<VulkanDevice>in_device,
VulkanSwapChain *swap_chain,
const std::vector<vk::DescriptorSetLayout> &descriptorSetLayouts,
vk::CommandPool &commandPool)
{
device = in_device;
vulkanSwapChain = swap_chain;
commandBufferManager = CommandBufferManager();
createTextures(commandPool);
createRenderPass();
createPushConstantRange();
createPipelines(descriptorSetLayouts);
createFramebuffer();
}
void DeferredRasterizer::shaderHotReload(const std::vector<vk::DescriptorSetLayout> &descriptor_set_layouts)
{
device->getLogicalDevice().destroyPipeline(geometryPipeline);
device->getLogicalDevice().destroyPipelineLayout(geometryPipelineLayout);
device->getLogicalDevice().destroyPipeline(lightingPipeline);
device->getLogicalDevice().destroyPipelineLayout(lightingPipelineLayout);
createPipelines(descriptor_set_layouts);
}
Kataglyphis::Texture &DeferredRasterizer::getOffscreenTexture(uint32_t index)
{
return *offscreenTextures[index];
}
void DeferredRasterizer::setPushConstant(PushConstantRasterizer push_constant)
{
pushConstant = push_constant;
}
void DeferredRasterizer::createTextures(vk::CommandPool &commandPool)
{
uint32_t count = vulkanSwapChain->getNumberSwapChainImages();
offscreenTextures.resize(count);
gBufferPositions.resize(count);
gBufferNormals.resize(count);
gBufferAlbedos.resize(count);
gBufferMaterials.resize(count);
vk::CommandBuffer cmdBuffer = CommandBufferManager::beginCommandBuffer(device->getLogicalDevice(), commandPool);
const vk::Extent2D &extent = vulkanSwapChain->getSwapChainExtent();
auto createAttachment = [&](std::vector<std::unique_ptr<Texture>>& textures, vk::Format format, vk::ImageUsageFlags usage) {
for (uint32_t i = 0; i < count; i++) {
auto tex = std::make_unique<Texture>();
tex->createImage(device, extent.width, extent.height, 1, format, vk::ImageTiling::eOptimal, usage, vk::MemoryPropertyFlagBits::eDeviceLocal);
tex->createImageView(device, format, vk::ImageAspectFlagBits::eColor, 1);
textures[i] = std::move(tex);
}
};
// Use specific formats for GBuffer
createAttachment(offscreenTextures, vk::Format::eR8G8B8A8Unorm, vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eStorage | vk::ImageUsageFlagBits::eTransferDst);
createAttachment(gBufferPositions, vk::Format::eR16G16B16A16Sfloat, vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eInputAttachment);
createAttachment(gBufferNormals, vk::Format::eR16G16B16A16Sfloat, vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eInputAttachment);
createAttachment(gBufferAlbedos, vk::Format::eR8G8B8A8Unorm, vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eInputAttachment);
createAttachment(gBufferMaterials, vk::Format::eR8G8B8A8Unorm, vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eInputAttachment);
// Depth buffer
depthBufferImage = std::make_unique<Texture>();
vk::Format depthFormat = Kataglyphis::choose_supported_format(device->getPhysicalDevice(), { vk::Format::eD32Sfloat, vk::Format::eD32SfloatS8Uint, vk::Format::eD24UnormS8Uint }, vk::ImageTiling::eOptimal, vk::FormatFeatureFlagBits::eDepthStencilAttachment);
depthBufferImage->createImage(device, extent.width, extent.height, 1, depthFormat, vk::ImageTiling::eOptimal, vk::ImageUsageFlagBits::eDepthStencilAttachment | vk::ImageUsageFlagBits::eInputAttachment, vk::MemoryPropertyFlagBits::eDeviceLocal);
depthBufferImage->createImageView(device, depthFormat, vk::ImageAspectFlagBits::eDepth, 1);
CommandBufferManager::endAndSubmitCommandBuffer(device->getLogicalDevice(), commandPool, device->getGraphicsQueue(), cmdBuffer);
}
void DeferredRasterizer::createPushConstantRange()
{
push_constant_range.stageFlags = vk::ShaderStageFlagBits::eAll;
push_constant_range.offset = 0;
push_constant_range.size = sizeof(PushConstantRasterizer);
}
void DeferredRasterizer::cleanUp()
{
spdlog::info("DeferredRasterizer: Destroying geometryPipeline: 0x{:x}, lightingPipeline: 0x{:x}", (uint64_t)(VkPipeline)geometryPipeline, (uint64_t)(VkPipeline)lightingPipeline);
auto logicalDevice = device->getLogicalDevice();
logicalDevice.destroyPipeline(geometryPipeline);
logicalDevice.destroyPipelineLayout(geometryPipelineLayout);
logicalDevice.destroyPipeline(lightingPipeline);
logicalDevice.destroyPipelineLayout(lightingPipelineLayout);
logicalDevice.destroyRenderPass(renderPass);
for (auto &fb : framebuffer) {
logicalDevice.destroyFramebuffer(fb);
}
framebuffer.clear();
for (auto& tex : offscreenTextures) { if (tex) tex->cleanUp(); }
offscreenTextures.clear();
for (auto& tex : gBufferPositions) { if (tex) tex->cleanUp(); }
gBufferPositions.clear();
for (auto& tex : gBufferNormals) { if (tex) tex->cleanUp(); }
gBufferNormals.clear();
for (auto& tex : gBufferAlbedos) { if (tex) tex->cleanUp(); }
gBufferAlbedos.clear();
for (auto& tex : gBufferMaterials) { if (tex) tex->cleanUp(); }
gBufferMaterials.clear();
if (depthBufferImage) { depthBufferImage->cleanUp(); }
depthBufferImage.reset();
}
DeferredRasterizer::~DeferredRasterizer() = default;
void Kataglyphis::VulkanRendererInternals::DeferredRasterizer::destroyFramebuffers()
{
for (auto &fb : framebuffer) {
spdlog::info("DeferredRasterizer: Destroying framebuffer: 0x{:x}", (uint64_t)(VkFramebuffer)fb);
device->getLogicalDevice().destroyFramebuffer(fb);
}
framebuffer.clear();
}
void Kataglyphis::VulkanRendererInternals::DeferredRasterizer::recreateFrameResources(vk::CommandPool commandPool)
{
for (auto& tex : offscreenTextures) { if (tex) tex->cleanUp(); }
offscreenTextures.clear();
for (auto& tex : gBufferPositions) { if (tex) tex->cleanUp(); }
gBufferPositions.clear();
for (auto& tex : gBufferNormals) { if (tex) tex->cleanUp(); }
gBufferNormals.clear();
for (auto& tex : gBufferAlbedos) { if (tex) tex->cleanUp(); }
gBufferAlbedos.clear();
for (auto& tex : gBufferMaterials) { if (tex) tex->cleanUp(); }
gBufferMaterials.clear();
if (depthBufferImage) { depthBufferImage->cleanUp(); }
depthBufferImage.reset();
createTextures(commandPool);
createFramebuffer();
}
void DeferredRasterizer::createRenderPass()
{
// Attachments
// 0: Final Color (Offscreen)
// 1: Position
// 2: Normal
// 3: Albedo
// 4: Material
// 5: Depth
vk::Format finalFormat = vk::Format::eR8G8B8A8Unorm;
vk::Format positionFormat = vk::Format::eR16G16B16A16Sfloat;
vk::Format normalFormat = vk::Format::eR16G16B16A16Sfloat;
vk::Format albedoFormat = vk::Format::eR8G8B8A8Unorm;
vk::Format materialFormat = vk::Format::eR8G8B8A8Unorm;
vk::Format depthFormat = Kataglyphis::choose_supported_format(device->getPhysicalDevice(), { vk::Format::eD32Sfloat, vk::Format::eD32SfloatS8Uint, vk::Format::eD24UnormS8Uint }, vk::ImageTiling::eOptimal, vk::FormatFeatureFlagBits::eDepthStencilAttachment);
auto createAttachmentDesc = [](vk::Format format, vk::ImageLayout finalLayout, [[maybe_unused]] bool isDepth = false) {
vk::AttachmentDescription desc;
desc.format = format;
desc.samples = vk::SampleCountFlagBits::e1;
desc.loadOp = vk::AttachmentLoadOp::eClear;
desc.storeOp = vk::AttachmentStoreOp::eStore;
desc.stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
desc.stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
desc.initialLayout = vk::ImageLayout::eUndefined;
desc.finalLayout = finalLayout;
return desc;
};
std::array<vk::AttachmentDescription, 6> attachments = {
createAttachmentDesc(finalFormat, vk::ImageLayout::eShaderReadOnlyOptimal), // 0: Final Output
createAttachmentDesc(positionFormat, vk::ImageLayout::eShaderReadOnlyOptimal), // 1: Position
createAttachmentDesc(normalFormat, vk::ImageLayout::eShaderReadOnlyOptimal), // 2: Normal
createAttachmentDesc(albedoFormat, vk::ImageLayout::eShaderReadOnlyOptimal), // 3: Albedo
createAttachmentDesc(materialFormat, vk::ImageLayout::eShaderReadOnlyOptimal), // 4: Material
createAttachmentDesc(depthFormat, vk::ImageLayout::eDepthStencilAttachmentOptimal, true) // 5: Depth
};
// Subpass 0: Geometry Pass
std::array<vk::AttachmentReference, 4> geometryColorRefs = {
vk::AttachmentReference{1, vk::ImageLayout::eColorAttachmentOptimal}, // Position
vk::AttachmentReference{2, vk::ImageLayout::eColorAttachmentOptimal}, // Normal
vk::AttachmentReference{3, vk::ImageLayout::eColorAttachmentOptimal}, // Albedo
vk::AttachmentReference{4, vk::ImageLayout::eColorAttachmentOptimal} // Material
};
vk::AttachmentReference geometryDepthRef{5, vk::ImageLayout::eDepthStencilAttachmentOptimal};
vk::SubpassDescription geometrySubpass;
geometrySubpass.pipelineBindPoint = vk::PipelineBindPoint::eGraphics;
geometrySubpass.colorAttachmentCount = static_cast<uint32_t>(geometryColorRefs.size());
geometrySubpass.pColorAttachments = geometryColorRefs.data();
geometrySubpass.pDepthStencilAttachment = &geometryDepthRef;
// Subpass 1: Lighting Pass
vk::AttachmentReference lightingColorRef{0, vk::ImageLayout::eColorAttachmentOptimal};
std::array<vk::AttachmentReference, 5> lightingInputRefs = {
vk::AttachmentReference{1, vk::ImageLayout::eShaderReadOnlyOptimal},
vk::AttachmentReference{2, vk::ImageLayout::eShaderReadOnlyOptimal},
vk::AttachmentReference{3, vk::ImageLayout::eShaderReadOnlyOptimal},
vk::AttachmentReference{4, vk::ImageLayout::eShaderReadOnlyOptimal},
vk::AttachmentReference{5, vk::ImageLayout::eShaderReadOnlyOptimal}
};
vk::SubpassDescription lightingSubpass;
lightingSubpass.pipelineBindPoint = vk::PipelineBindPoint::eGraphics;
lightingSubpass.colorAttachmentCount = 1;
lightingSubpass.pColorAttachments = &lightingColorRef;
lightingSubpass.inputAttachmentCount = static_cast<uint32_t>(lightingInputRefs.size());
lightingSubpass.pInputAttachments = lightingInputRefs.data();
// Dependencies
std::array<vk::SubpassDependency, 3> dependencies;
// External -> Geometry Subpass
dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
dependencies[0].dstSubpass = 0;
dependencies[0].srcStageMask = vk::PipelineStageFlagBits::eBottomOfPipe;
dependencies[0].dstStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput | vk::PipelineStageFlagBits::eEarlyFragmentTests;
dependencies[0].srcAccessMask = vk::AccessFlagBits::eMemoryRead;
dependencies[0].dstAccessMask = vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite | vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentWrite;
dependencies[0].dependencyFlags = vk::DependencyFlagBits::eByRegion;
// Geometry Subpass -> Lighting Subpass
dependencies[1].srcSubpass = 0;
dependencies[1].dstSubpass = 1;
dependencies[1].srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput | vk::PipelineStageFlagBits::eLateFragmentTests;
dependencies[1].dstStageMask = vk::PipelineStageFlagBits::eFragmentShader;
dependencies[1].srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite | vk::AccessFlagBits::eDepthStencilAttachmentWrite;
dependencies[1].dstAccessMask = vk::AccessFlagBits::eInputAttachmentRead;
dependencies[1].dependencyFlags = vk::DependencyFlagBits::eByRegion;
// Lighting Subpass -> External
dependencies[2].srcSubpass = 1;
dependencies[2].dstSubpass = VK_SUBPASS_EXTERNAL;
dependencies[2].srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput;
dependencies[2].dstStageMask = vk::PipelineStageFlagBits::eBottomOfPipe;
dependencies[2].srcAccessMask = vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite;
dependencies[2].dstAccessMask = vk::AccessFlagBits::eMemoryRead;
dependencies[2].dependencyFlags = vk::DependencyFlagBits::eByRegion;
std::array<vk::SubpassDescription, 2> subpasses = {geometrySubpass, lightingSubpass};
vk::RenderPassCreateInfo renderPassInfo;
renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
renderPassInfo.pAttachments = attachments.data();
renderPassInfo.subpassCount = static_cast<uint32_t>(subpasses.size());
renderPassInfo.pSubpasses = subpasses.data();
renderPassInfo.dependencyCount = static_cast<uint32_t>(dependencies.size());
renderPassInfo.pDependencies = dependencies.data();
auto result = device->getLogicalDevice().createRenderPass(renderPassInfo);
ASSERT_VULKAN(VkResult(result.result), "Failed to create deferred render pass!")
renderPass = result.value;
}
void DeferredRasterizer::createPipelines(const std::vector<vk::DescriptorSetLayout> &descriptorSetLayouts)
{
// Basic setup for geometry pipeline
std::filesystem::path cwd = std::filesystem::current_path();
std::stringstream shader_dir;
shader_dir << cwd.string() << RELATIVE_RESOURCE_PATH << "Shaders/deferred/";
ShaderHelper shaderHelper;
shaderHelper.compileShader(shader_dir.str(), "geometry.vert");
shaderHelper.compileShader(shader_dir.str(), "geometry.frag");
shaderHelper.compileShader(shader_dir.str(), "lighting.vert");
shaderHelper.compileShader(shader_dir.str(), "lighting.frag");
// Implement actual pipeline creation here...
File geomVertFile(shaderHelper.getShaderSpvDir(shader_dir.str(), "geometry.vert"));
File geomFragFile(shaderHelper.getShaderSpvDir(shader_dir.str(), "geometry.frag"));
vk::ShaderModule geomVertModule = shaderHelper.createShaderModule(device, geomVertFile.readCharSequence());
vk::ShaderModule geomFragModule = shaderHelper.createShaderModule(device, geomFragFile.readCharSequence());
vk::PipelineShaderStageCreateInfo geomVertStage{ {}, vk::ShaderStageFlagBits::eVertex, geomVertModule, "main" };
vk::PipelineShaderStageCreateInfo geomFragStage{ {}, vk::ShaderStageFlagBits::eFragment, geomFragModule, "main" };
std::array<vk::PipelineShaderStageCreateInfo, 2> geomStages = { geomVertStage, geomFragStage };
vk::VertexInputBindingDescription bindingDescription;
bindingDescription.binding = 0;
bindingDescription.stride = sizeof(Vertex);
bindingDescription.inputRate = vk::VertexInputRate::eVertex;
std::array<vk::VertexInputAttributeDescription, 4> attributeDescriptions = vertex::getVertexInputAttributeDesc();
vk::PipelineVertexInputStateCreateInfo vertexInputInfo{};
vertexInputInfo.vertexBindingDescriptionCount = 1;
vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
vertexInputInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size());
vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data();
vk::PipelineInputAssemblyStateCreateInfo inputAssembly{};
inputAssembly.topology = vk::PrimitiveTopology::eTriangleList;
inputAssembly.primitiveRestartEnable = VK_FALSE;
vk::PipelineViewportStateCreateInfo viewportState{};
viewportState.viewportCount = 1;
viewportState.pViewports = nullptr;
viewportState.scissorCount = 1;
viewportState.pScissors = nullptr;
std::vector<vk::DynamicState> dynamicStates = { vk::DynamicState::eViewport, vk::DynamicState::eScissor };
vk::PipelineDynamicStateCreateInfo dynamicState{};
dynamicState.dynamicStateCount = static_cast<uint32_t>(dynamicStates.size());
dynamicState.pDynamicStates = dynamicStates.data();
vk::PipelineRasterizationStateCreateInfo rasterizer{};
rasterizer.depthClampEnable = VK_FALSE;
rasterizer.rasterizerDiscardEnable = VK_FALSE;
rasterizer.polygonMode = vk::PolygonMode::eFill;
rasterizer.lineWidth = 1.0f;
rasterizer.cullMode = vk::CullModeFlagBits::eBack;
rasterizer.frontFace = vk::FrontFace::eCounterClockwise;
rasterizer.depthBiasEnable = VK_FALSE;
vk::PipelineMultisampleStateCreateInfo multisampling{};
multisampling.sampleShadingEnable = VK_FALSE;
multisampling.rasterizationSamples = vk::SampleCountFlagBits::e1;
vk::PipelineDepthStencilStateCreateInfo depthStencil{};
depthStencil.depthTestEnable = VK_TRUE;
depthStencil.depthWriteEnable = VK_TRUE;
depthStencil.depthCompareOp = vk::CompareOp::eLess;
depthStencil.depthBoundsTestEnable = VK_FALSE;
depthStencil.stencilTestEnable = VK_FALSE;
vk::PipelineColorBlendAttachmentState colorBlendAttachment{};
colorBlendAttachment.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA;
colorBlendAttachment.blendEnable = VK_FALSE;
std::array<vk::PipelineColorBlendAttachmentState, 4> colorBlendAttachments = {
colorBlendAttachment, colorBlendAttachment, colorBlendAttachment, colorBlendAttachment
};
vk::PipelineColorBlendStateCreateInfo colorBlending{};
colorBlending.logicOpEnable = VK_FALSE;
colorBlending.attachmentCount = static_cast<uint32_t>(colorBlendAttachments.size());
colorBlending.pAttachments = colorBlendAttachments.data();
vk::PipelineLayoutCreateInfo pipelineLayoutInfo{};
pipelineLayoutInfo.setLayoutCount = static_cast<uint32_t>(descriptorSetLayouts.size());
pipelineLayoutInfo.pSetLayouts = descriptorSetLayouts.data();
pipelineLayoutInfo.pushConstantRangeCount = 1;
pipelineLayoutInfo.pPushConstantRanges = &push_constant_range;
geometryPipelineLayout = device->getLogicalDevice().createPipelineLayout(pipelineLayoutInfo).value;
vk::GraphicsPipelineCreateInfo pipelineInfo{};
pipelineInfo.stageCount = geomStages.size();
pipelineInfo.pStages = geomStages.data();
pipelineInfo.pVertexInputState = &vertexInputInfo;
pipelineInfo.pInputAssemblyState = &inputAssembly;
pipelineInfo.pViewportState = &viewportState;
pipelineInfo.pRasterizationState = &rasterizer;
pipelineInfo.pMultisampleState = &multisampling;
pipelineInfo.pDepthStencilState = &depthStencil;
pipelineInfo.pColorBlendState = &colorBlending;
pipelineInfo.pDynamicState = &dynamicState;
pipelineInfo.layout = geometryPipelineLayout;
pipelineInfo.renderPass = renderPass;
pipelineInfo.subpass = 0;
geometryPipeline = device->getLogicalDevice().createGraphicsPipeline(nullptr, pipelineInfo).value;
spdlog::info("DeferredRasterizer: Created geometryPipeline: 0x{:x}", (uint64_t)(VkPipeline)geometryPipeline);
device->getLogicalDevice().destroyShaderModule(geomVertModule);
device->getLogicalDevice().destroyShaderModule(geomFragModule);
// Lighting Pipeline
File lightVertFile(shaderHelper.getShaderSpvDir(shader_dir.str(), "lighting.vert"));
File lightFragFile(shaderHelper.getShaderSpvDir(shader_dir.str(), "lighting.frag"));
vk::ShaderModule lightVertModule = shaderHelper.createShaderModule(device, lightVertFile.readCharSequence());
vk::ShaderModule lightFragModule = shaderHelper.createShaderModule(device, lightFragFile.readCharSequence());
vk::PipelineShaderStageCreateInfo lightVertStage{ {}, vk::ShaderStageFlagBits::eVertex, lightVertModule, "main" };
vk::PipelineShaderStageCreateInfo lightFragStage{ {}, vk::ShaderStageFlagBits::eFragment, lightFragModule, "main" };
std::array<vk::PipelineShaderStageCreateInfo, 2> lightStages = { lightVertStage, lightFragStage };
vk::PipelineVertexInputStateCreateInfo emptyVertexInputInfo{};
vk::VertexInputBindingDescription dummyBinding{};
dummyBinding.binding = 0;
dummyBinding.stride = 16;
dummyBinding.inputRate = vk::VertexInputRate::eVertex;
vk::VertexInputAttributeDescription dummyAttribute{};
dummyAttribute.binding = 0;
dummyAttribute.location = 0;
dummyAttribute.format = vk::Format::eR32G32B32A32Sfloat;
dummyAttribute.offset = 0;
emptyVertexInputInfo.vertexBindingDescriptionCount = 1;
emptyVertexInputInfo.pVertexBindingDescriptions = &dummyBinding;
emptyVertexInputInfo.vertexAttributeDescriptionCount = 1;
emptyVertexInputInfo.pVertexAttributeDescriptions = &dummyAttribute;
rasterizer.cullMode = vk::CullModeFlagBits::eNone;
depthStencil.depthTestEnable = VK_FALSE;
depthStencil.depthWriteEnable = VK_FALSE;
vk::PipelineColorBlendStateCreateInfo lightColorBlending{};
lightColorBlending.logicOpEnable = VK_FALSE;
lightColorBlending.attachmentCount = 1;
lightColorBlending.pAttachments = &colorBlendAttachment;
vk::PipelineLayoutCreateInfo lightPipelineLayoutInfo{};
lightPipelineLayoutInfo.setLayoutCount = static_cast<uint32_t>(descriptorSetLayouts.size());
lightPipelineLayoutInfo.pSetLayouts = descriptorSetLayouts.data();
// No push constants for lighting pass usually, or same if needed
lightingPipelineLayout = device->getLogicalDevice().createPipelineLayout(lightPipelineLayoutInfo).value;
vk::GraphicsPipelineCreateInfo lightingPipelineInfo{};
lightingPipelineInfo.stageCount = lightStages.size();
lightingPipelineInfo.pStages = lightStages.data();
lightingPipelineInfo.pVertexInputState = &emptyVertexInputInfo;
lightingPipelineInfo.pInputAssemblyState = &inputAssembly;
lightingPipelineInfo.pViewportState = &viewportState;
lightingPipelineInfo.pRasterizationState = &rasterizer;
lightingPipelineInfo.pMultisampleState = &multisampling;
lightingPipelineInfo.pDepthStencilState = &depthStencil;
lightingPipelineInfo.pColorBlendState = &lightColorBlending;
lightingPipelineInfo.pDynamicState = &dynamicState;
lightingPipelineInfo.layout = lightingPipelineLayout;
lightingPipelineInfo.renderPass = renderPass;
lightingPipelineInfo.subpass = 1;
lightingPipeline = device->getLogicalDevice().createGraphicsPipeline(nullptr, lightingPipelineInfo).value;
spdlog::info("DeferredRasterizer: Created lightingPipeline: 0x{:x}", (uint64_t)(VkPipeline)lightingPipeline);
device->getLogicalDevice().destroyShaderModule(lightVertModule);
device->getLogicalDevice().destroyShaderModule(lightFragModule);
}
void DeferredRasterizer::createFramebuffer()
{
std::array<vk::ImageView, 6> attachments;
const vk::Extent2D &extent = vulkanSwapChain->getSwapChainExtent();
vk::FramebufferCreateInfo framebufferInfo;
framebufferInfo.renderPass = renderPass;
framebufferInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
framebufferInfo.pAttachments = attachments.data();
framebufferInfo.width = extent.width;
framebufferInfo.height = extent.height;
framebufferInfo.layers = 1;
framebuffer.resize(vulkanSwapChain->getNumberSwapChainImages());
for (uint32_t i = 0; i < vulkanSwapChain->getNumberSwapChainImages(); i++) {
attachments[0] = offscreenTextures[i]->getImageView();
attachments[1] = gBufferPositions[i]->getImageView();
attachments[2] = gBufferNormals[i]->getImageView();
attachments[3] = gBufferAlbedos[i]->getImageView();
attachments[4] = gBufferMaterials[i]->getImageView();
attachments[5] = depthBufferImage->getImageView();
auto result = device->getLogicalDevice().createFramebuffer(framebufferInfo);
ASSERT_VULKAN(VkResult(result.result), "Failed to create deferred framebuffer!");
framebuffer[i] = result.value;
spdlog::info("DeferredRasterizer: Created framebuffer[{}]: 0x{:x}", i, (uint64_t)(VkFramebuffer)framebuffer[i]);
}
}
void DeferredRasterizer::recordCommands(vk::CommandBuffer &commandBuffer, uint32_t image_index, Kataglyphis::Scene *scene, const std::vector<vk::DescriptorSet> &descriptorSets)
{
vk::RenderPassBeginInfo renderPassInfo{};
renderPassInfo.renderPass = renderPass;
renderPassInfo.framebuffer = framebuffer[image_index];
renderPassInfo.renderArea.offset = vk::Offset2D{0, 0};
renderPassInfo.renderArea.extent = vulkanSwapChain->getSwapChainExtent();
std::array<vk::ClearValue, 6> clearValues{};
clearValues[0].color = vk::ClearColorValue{std::array<float, 4>{0.0f, 0.0f, 0.0f, 0.0f}};
clearValues[1].color = vk::ClearColorValue{std::array<float, 4>{0.0f, 0.0f, 0.0f, 0.0f}};
clearValues[2].color = vk::ClearColorValue{std::array<float, 4>{0.0f, 0.0f, 0.0f, 0.0f}};
clearValues[3].color = vk::ClearColorValue{std::array<float, 4>{0.0f, 0.0f, 0.0f, 1.0f}};
clearValues[4].color = vk::ClearColorValue{std::array<float, 4>{0.0f, 0.0f, 0.0f, 1.0f}};
clearValues[5].depthStencil = vk::ClearDepthStencilValue{1.0f, 0};
renderPassInfo.clearValueCount = static_cast<uint32_t>(clearValues.size());
renderPassInfo.pClearValues = clearValues.data();
commandBuffer.beginRenderPass(renderPassInfo, vk::SubpassContents::eInline);
vk::Viewport viewport{};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = static_cast<float>(vulkanSwapChain->getSwapChainExtent().width);
viewport.height = static_cast<float>(vulkanSwapChain->getSwapChainExtent().height);
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
commandBuffer.setViewport(0, 1, &viewport);
vk::Rect2D scissor{};
scissor.offset = vk::Offset2D{ 0, 0 };
scissor.extent = vulkanSwapChain->getSwapChainExtent();
commandBuffer.setScissor(0, 1, &scissor);
// Subpass 0: Geometry
commandBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, geometryPipeline);
for (uint32_t m = 0; m < scene->getModelCount(); m++) {
pushConstant.model = scene->getModelMatrix(m);
commandBuffer.pushConstants(
geometryPipelineLayout, vk::ShaderStageFlagBits::eAll, 0, sizeof(PushConstantRasterizer), &pushConstant);
for (unsigned int k = 0; k < scene->getMeshCount(m); k++) {
std::vector<vk::Buffer> const vertex_buffers = { scene->getVertexBuffer(m, k) };
vk::DeviceSize offsets[] = { 0 };
commandBuffer.bindVertexBuffers(0, vertex_buffers, offsets);
commandBuffer.bindIndexBuffer(scene->getIndexBuffer(m, k), 0, vk::IndexType::eUint32);
// Bind global descriptor set (set 0)
commandBuffer.bindDescriptorSets(
vk::PipelineBindPoint::eGraphics, geometryPipelineLayout, 0, 1, &descriptorSets[0], 0, nullptr);
commandBuffer.drawIndexed(scene->getIndexCount(m, k), 1, 0, 0, 0);
}
}
// Transition to Subpass 1: Lighting
commandBuffer.nextSubpass(vk::SubpassContents::eInline);
commandBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, lightingPipeline);
// Bind lighting pass specific descriptor set (set 1) alongside global (set 0) if needed
// Assuming descriptorSets[0] is global, descriptorSets[1] is GBuffer inputs
if (descriptorSets.size() > 1) {
commandBuffer.bindDescriptorSets(
vk::PipelineBindPoint::eGraphics, lightingPipelineLayout, 1, 1, &descriptorSets[1], 0, nullptr);
}
commandBuffer.bindDescriptorSets(
vk::PipelineBindPoint::eGraphics, lightingPipelineLayout, 0, 1, &descriptorSets[0], 0, nullptr);
commandBuffer.draw(3, 1, 0, 0); // Fullscreen triangle
commandBuffer.endRenderPass();
}