Program Listing for File PathTracing.cpp
↰ Return to documentation for file (Src/GraphicsEngineVulkan/renderer/PathTracing.cpp
)
#include "PathTracing.hpp"
#include <algorithm>
#include <array>
#include <filesystem>
#include "util/File.hpp"
#include "vulkan_base/ShaderHelper.hpp"
#include "common/Utilities.hpp"
#include "renderer/VulkanRendererConfig.hpp"
// Good source:
// https://github.com/nvpro-samples/vk_mini_path_tracer/blob/main/vk_mini_path_tracer/main.cpp
Kataglyphis::VulkanRendererInternals::PathTracing::PathTracing() {}
void Kataglyphis::VulkanRendererInternals::PathTracing::init(VulkanDevice *device,
const std::vector<VkDescriptorSetLayout> &descriptorSetLayouts)
{
this->device = device;
VkPhysicalDeviceProperties physicalDeviceProps = device->getPhysicalDeviceProperties();
timeStampPeriod = physicalDeviceProps.limits.timestampPeriod;
// save the limits for handling all special cases later on
computeLimits.maxComputeWorkGroupCount[0] = physicalDeviceProps.limits.maxComputeWorkGroupCount[0];
computeLimits.maxComputeWorkGroupCount[1] = physicalDeviceProps.limits.maxComputeWorkGroupCount[1];
computeLimits.maxComputeWorkGroupCount[2] = physicalDeviceProps.limits.maxComputeWorkGroupCount[2];
computeLimits.maxComputeWorkGroupInvocations = physicalDeviceProps.limits.maxComputeWorkGroupInvocations;
computeLimits.maxComputeWorkGroupSize[0] = physicalDeviceProps.limits.maxComputeWorkGroupSize[0];
computeLimits.maxComputeWorkGroupSize[1] = physicalDeviceProps.limits.maxComputeWorkGroupSize[1];
computeLimits.maxComputeWorkGroupSize[2] = physicalDeviceProps.limits.maxComputeWorkGroupSize[2];
queryResults.resize(query_count);
createQueryPool();
createPipeline(descriptorSetLayouts);
}
void Kataglyphis::VulkanRendererInternals::PathTracing::shaderHotReload(
const std::vector<VkDescriptorSetLayout> &descriptor_set_layouts)
{
vkDestroyPipeline(device->getLogicalDevice(), pipeline, nullptr);
createPipeline(descriptor_set_layouts);
}
void Kataglyphis::VulkanRendererInternals::PathTracing::recordCommands(VkCommandBuffer &commandBuffer,
uint32_t image_index,
VulkanImage &vulkanImage,
VulkanSwapChain *vulkanSwapChain,
const std::vector<VkDescriptorSet> &descriptorSets)
{
// we have reset the pool; hence start by 0
uint32_t query = 0;
vkCmdResetQueryPool(commandBuffer, queryPool, 0, query_count);
vkCmdWriteTimestamp(
commandBuffer, VkPipelineStageFlagBits::VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, queryPool, query++);
Kataglyphis::VulkanRendererInternals::QueueFamilyIndices indices = device->getQueueFamilies();
VkImageSubresourceRange subresourceRange{};
subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subresourceRange.baseMipLevel = 0;
subresourceRange.baseArrayLayer = 0;
subresourceRange.levelCount = 1;
subresourceRange.layerCount = 1;
VkImageMemoryBarrier presentToPathTracingImageBarrier{};
presentToPathTracingImageBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
presentToPathTracingImageBarrier.pNext = nullptr;
presentToPathTracingImageBarrier.srcQueueFamilyIndex = indices.graphics_family;
presentToPathTracingImageBarrier.dstQueueFamilyIndex = indices.compute_family;
presentToPathTracingImageBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
presentToPathTracingImageBarrier.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
presentToPathTracingImageBarrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
presentToPathTracingImageBarrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
presentToPathTracingImageBarrier.subresourceRange = subresourceRange;
presentToPathTracingImageBarrier.image = vulkanImage.getImage();
vkCmdPipelineBarrier(commandBuffer,
VK_PIPELINE_STAGE_VERTEX_SHADER_BIT,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
0,
0,
nullptr,
0,
nullptr,
1,
&presentToPathTracingImageBarrier);
VkExtent2D imageSize = vulkanSwapChain->getSwapChainExtent();
push_constant.width = imageSize.width;
push_constant.height = imageSize.height;
push_constant.clearColor = { 0.2f, 0.65f, 0.4f, 1.0f };
vkCmdPushConstants(
commandBuffer, pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(PushConstantPathTracing), &push_constant);
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline);
vkCmdBindDescriptorSets(commandBuffer,
VK_PIPELINE_BIND_POINT_COMPUTE,
pipeline_layout,
0,
static_cast<uint32_t>(descriptorSets.size()),
descriptorSets.data(),
0,
0);
uint32_t workGroupCountX = std::max(
(imageSize.width + specializationData.specWorkGroupSizeX - 1) / specializationData.specWorkGroupSizeX, 1U);
uint32_t workGroupCountY = std::max(
(imageSize.height + specializationData.specWorkGroupSizeY - 1) / specializationData.specWorkGroupSizeY, 1U);
uint32_t workGroupCountZ = 1;
vkCmdDispatch(commandBuffer, workGroupCountX, workGroupCountY, workGroupCountZ);
VkImageMemoryBarrier pathTracingToPresentImageBarrier{};
pathTracingToPresentImageBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
pathTracingToPresentImageBarrier.pNext = nullptr;
pathTracingToPresentImageBarrier.srcQueueFamilyIndex = indices.compute_family;
pathTracingToPresentImageBarrier.dstQueueFamilyIndex = indices.graphics_family;
pathTracingToPresentImageBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
pathTracingToPresentImageBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
pathTracingToPresentImageBarrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
pathTracingToPresentImageBarrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
pathTracingToPresentImageBarrier.image = vulkanImage.getImage();
pathTracingToPresentImageBarrier.subresourceRange = subresourceRange;
vkCmdPipelineBarrier(commandBuffer,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
VK_PIPELINE_STAGE_VERTEX_SHADER_BIT,
0,
0,
nullptr,
0,
nullptr,
1,
&pathTracingToPresentImageBarrier);
vkCmdWriteTimestamp(
commandBuffer, VkPipelineStageFlagBits::VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, queryPool, query++);
VkResult result = vkGetQueryPoolResults(device->getLogicalDevice(),
queryPool,
0,
query_count,
queryResults.size() * sizeof(uint64_t),
queryResults.data(),
static_cast<VkDeviceSize>(sizeof(uint64_t)),
VK_QUERY_RESULT_64_BIT);
if (result != VK_NOT_READY) {
pathTracingTiming = (static_cast<float>(queryResults[1] - queryResults[0]) * timeStampPeriod) / 1000000.f;
}
}
void Kataglyphis::VulkanRendererInternals::PathTracing::cleanUp()
{
vkDestroyPipeline(device->getLogicalDevice(), pipeline, nullptr);
vkDestroyPipelineLayout(device->getLogicalDevice(), pipeline_layout, nullptr);
vkDestroyQueryPool(device->getLogicalDevice(), queryPool, nullptr);
}
Kataglyphis::VulkanRendererInternals::PathTracing::~PathTracing() {}
void Kataglyphis::VulkanRendererInternals::PathTracing::createQueryPool()
{
VkQueryPoolCreateInfo queryPoolInfo = {};
queryPoolInfo.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
// This query pool will store pipeline statistics
queryPoolInfo.queryType = VK_QUERY_TYPE_TIMESTAMP;
// Pipeline counters to be returned for this pool
queryPoolInfo.pipelineStatistics = VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT;
queryPoolInfo.queryCount = query_count;
ASSERT_VULKAN(
vkCreateQueryPool(device->getLogicalDevice(), &queryPoolInfo, NULL, &queryPool), "Failed to create query pool!");
}
void Kataglyphis::VulkanRendererInternals::PathTracing::createPipeline(
const std::vector<VkDescriptorSetLayout> &descriptorSetLayouts)
{
VkPushConstantRange push_constant_range{};
push_constant_range.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
push_constant_range.offset = 0;
push_constant_range.size = sizeof(PushConstantPathTracing);
VkPipelineLayoutCreateInfo compute_pipeline_layout_create_info{};
compute_pipeline_layout_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
compute_pipeline_layout_create_info.setLayoutCount = static_cast<uint32_t>(descriptorSetLayouts.size());
compute_pipeline_layout_create_info.pushConstantRangeCount = 1;
compute_pipeline_layout_create_info.pPushConstantRanges = &push_constant_range;
compute_pipeline_layout_create_info.pSetLayouts = descriptorSetLayouts.data();
ASSERT_VULKAN(vkCreatePipelineLayout(
device->getLogicalDevice(), &compute_pipeline_layout_create_info, nullptr, &pipeline_layout),
"Failed to create compute path tracing pipeline layout!");
// create pipeline
std::stringstream pathTracing_shader_dir;
std::filesystem::path cwd = std::filesystem::current_path();
pathTracing_shader_dir << cwd.string();
pathTracing_shader_dir << RELATIVE_RESOURCE_PATH;
pathTracing_shader_dir << "Shaders/path_tracing/";
std::string pathTracing_shader = "path_tracing.comp";
ShaderHelper shaderHelper;
File pathTracingShaderFile(shaderHelper.getShaderSpvDir(pathTracing_shader_dir.str(), pathTracing_shader));
std::vector<char> pathTracingShadercode = pathTracingShaderFile.readCharSequence();
shaderHelper.compileShader(pathTracing_shader_dir.str(), pathTracing_shader);
// build shader modules to link to graphics pipeline
VkShaderModule pathTracingModule = shaderHelper.createShaderModule(device, pathTracingShadercode);
// Specialization constant for workgroup size
std::array<VkSpecializationMapEntry, 2> specEntries{};
specEntries[0].constantID = 0;
specEntries[0].size = sizeof(specializationData.specWorkGroupSizeX);
specEntries[0].offset = 0;
specEntries[1].constantID = 1;
specEntries[1].size = sizeof(specializationData.specWorkGroupSizeY);
specEntries[1].offset = offsetof(SpecializationData, specWorkGroupSizeY);
// specEntries[2].constantID = 2;
// specEntries[2].size = sizeof(specializationData.specWorkGroupSizeZ);
// specEntries[2].offset = offsetof(SpecializationData, specWorkGroupSizeZ);
VkSpecializationInfo specInfo{};
specInfo.dataSize = sizeof(specializationData);
specInfo.mapEntryCount = static_cast<uint32_t>(specEntries.size());
specInfo.pMapEntries = specEntries.data();
specInfo.pData = &specializationData;
VkPipelineShaderStageCreateInfo compute_shader_integrate_create_info{};
compute_shader_integrate_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
compute_shader_integrate_create_info.stage = VK_SHADER_STAGE_COMPUTE_BIT;
compute_shader_integrate_create_info.module = pathTracingModule;
compute_shader_integrate_create_info.pSpecializationInfo = &specInfo;
compute_shader_integrate_create_info.pName = "main";
// -- COMPUTE PIPELINE CREATION --
VkComputePipelineCreateInfo compute_pipeline_create_info{};
compute_pipeline_create_info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
compute_pipeline_create_info.stage = compute_shader_integrate_create_info;
compute_pipeline_create_info.layout = pipeline_layout;
compute_pipeline_create_info.flags = 0;
// create compute pipeline
ASSERT_VULKAN(vkCreateComputePipelines(
device->getLogicalDevice(), VK_NULL_HANDLE, 1, &compute_pipeline_create_info, nullptr, &pipeline),
"Failed to create a compute pipeline!");
// Destroy shader modules, no longer needed after pipeline created
vkDestroyShaderModule(device->getLogicalDevice(), pathTracingModule, nullptr);
}