diff --git a/res/shaders/Miss.slang b/res/shaders/Miss.slang new file mode 100644 index 0000000..3edfc62 --- /dev/null +++ b/res/shaders/Miss.slang @@ -0,0 +1,8 @@ +import Common; + +[shader("miss")] +void miss(inout RayPayload p) +{ + p.light = float3(0, 0, 0); + p.hit = false; +} \ No newline at end of file diff --git a/src/gpu/GPURenderer.cpp b/src/gpu/GPURenderer.cpp index 7edf934..60e3df3 100644 --- a/src/gpu/GPURenderer.cpp +++ b/src/gpu/GPURenderer.cpp @@ -1,4 +1,5 @@ #include "GPURenderer.h" +#include "util/ModelLoader.h" #include "vulkan/vulkan_enums.hpp" #include "vulkan/vulkan_handles.hpp" #include "vulkan/vulkan_raii.hpp" @@ -9,16 +10,11 @@ #include "vk_mem_alloc.h" GPURenderer::GPURenderer() - : instance(nullptr), physicalDevice(nullptr), device(nullptr), queue(nullptr), cmdPool(nullptr), cmdBuffers(nullptr), - descriptorLayout(nullptr), descriptorSet(nullptr), descriptorPool(nullptr), pipelineLayout(nullptr), rayGen(nullptr), - closestHit(nullptr), miss(nullptr), pipeline(nullptr), radianceAccumulator(nullptr), radianceAllocation(nullptr), image(nullptr), - imageAllocation(nullptr) - { createDevice(); createCommands(); createDescriptors(); - createShaders(); + createPipeline(); } GPURenderer::~GPURenderer() {} @@ -40,6 +36,12 @@ void GPURenderer::createDevice() } } } + auto properties = physicalDevice.getProperties2(); + + accelerationProperties = properties.get(); + rayTracingProperties = properties.get(); + uint32_t computeQueueFamily = 0; auto queueProps = physicalDevice.getQueueFamilyProperties(); for (uint32_t i = 0; i < queueProps.size(); ++i) @@ -50,22 +52,31 @@ void GPURenderer::createDevice() break; } } - float queuePriority = 0.0f; - vk::DeviceQueueCreateInfo deviceQueueCreateInfo({}, computeQueueFamily, 1, &queuePriority); - vk::DeviceCreateInfo deviceCreateInfo({}, deviceQueueCreateInfo); + std::vector queuePriority = {1.0f}; + auto featureChain = physicalDevice.getFeatures2(); + auto features = featureChain.get(); + + vk::DeviceQueueCreateInfo deviceQueueCreateInfo({}, computeQueueFamily, queuePriority); + const char* extensions[] = {vk::KHRAccelerationStructureExtensionName, vk::KHRRayTracingPipelineExtensionName, vk::KHRDeferredHostOperationsExtensionName}; + vk::DeviceCreateInfo deviceCreateInfo({}, deviceQueueCreateInfo, {}, extensions, nullptr, &features); device = Device(physicalDevice, deviceCreateInfo); - VmaVulkanFunctions vulkanFunctions = {}; - vulkanFunctions.vkGetInstanceProcAddr = &vkGetInstanceProcAddr; - vulkanFunctions.vkGetDeviceProcAddr = &vkGetDeviceProcAddr; + queue = Queue(device, computeQueueFamily, 0); - VmaAllocatorCreateInfo allocatorCreateInfo = {}; - allocatorCreateInfo.flags = VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT; - allocatorCreateInfo.vulkanApiVersion = VK_API_VERSION_1_2; - allocatorCreateInfo.physicalDevice = *physicalDevice; - allocatorCreateInfo.device = *device; - allocatorCreateInfo.instance = *instance; - allocatorCreateInfo.pVulkanFunctions = &vulkanFunctions; + VmaVulkanFunctions vulkanFunctions = { + .vkGetInstanceProcAddr = &vkGetInstanceProcAddr, + .vkGetDeviceProcAddr = &vkGetDeviceProcAddr, + }; + + VmaAllocatorCreateInfo allocatorCreateInfo = { + .flags = VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT | VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT, + .physicalDevice = *physicalDevice, + .device = *device, + .pVulkanFunctions = &vulkanFunctions, + .instance = *instance, + .vulkanApiVersion = VK_API_VERSION_1_3, + }; vmaCreateAllocator(&allocatorCreateInfo, &allocator); } @@ -121,33 +132,40 @@ void GPURenderer::createDescriptors() descriptorLayout = DescriptorSetLayout(device, descriptorSetLayoutCreateInfo); auto descriptorPoolSizes = { -vk::DescriptorPoolSize(vk::DescriptorType::eUniformBuffer, 1), -vk::DescriptorPoolSize(vk::DescriptorType::eAccelerationStructureKHR, 1), -vk::DescriptorPoolSize(vk::DescriptorType::eStorageImage, 2), -vk::DescriptorPoolSize(vk::DescriptorType::eStorageBuffer, 8), + vk::DescriptorPoolSize(vk::DescriptorType::eUniformBuffer, 1), + vk::DescriptorPoolSize(vk::DescriptorType::eAccelerationStructureKHR, 1), + vk::DescriptorPoolSize(vk::DescriptorType::eStorageImage, 2), + vk::DescriptorPoolSize(vk::DescriptorType::eStorageBuffer, 8), }; - descriptorPool = DescriptorPool(device, vk::DescriptorPoolCreateInfo({}, 4, descriptorPoolSizes)); + descriptorPool = + DescriptorPool(device, vk::DescriptorPoolCreateInfo({vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet}, 4, descriptorPoolSizes)); // create a PipelineLayout using that DescriptorSetLayout - vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo({}, *descriptorLayout); + vk::PushConstantRange range = + vk::PushConstantRange(vk::ShaderStageFlagBits::eRaygenKHR | vk::ShaderStageFlagBits::eClosestHitKHR, 0, sizeof(SampleParams)); + vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo({}, *descriptorLayout, range); pipelineLayout = PipelineLayout(device, pipelineLayoutCreateInfo); } using namespace slang; -void GPURenderer::createShaders() +template constexpr T align(T size, T alignment) { return (size + alignment - 1) & ~(alignment - 1); } + +void GPURenderer::createPipeline() { Slang::ComPtr globalSession; createGlobalSession(globalSession.writeRef()); - SessionDesc sessionDesc; - TargetDesc targetDesc; - targetDesc.format = SLANG_SPIRV; - targetDesc.profile = globalSession->findProfile("glsl_450"); - sessionDesc.targets = &targetDesc; - sessionDesc.targetCount = 1; - const char* searchPaths[] = {"res/shaders/"}; - sessionDesc.searchPaths = searchPaths; - sessionDesc.searchPathCount = 1; + TargetDesc targetDesc = { + .format = SLANG_SPIRV, + .profile = globalSession->findProfile("glsl_450"), + }; + const char* searchPaths[] = {"../res/shaders/"}; + SessionDesc sessionDesc = { + .targets = &targetDesc, + .targetCount = 1, + .searchPaths = searchPaths, + .searchPathCount = 1, + }; Slang::ComPtr session; globalSession->createSession(sessionDesc, session.writeRef()); @@ -158,7 +176,7 @@ void GPURenderer::createShaders() std::cout << (const char*)diagnostics->getBufferPointer() << std::endl; } Slang::ComPtr rayGenEntry; - raygenModule->findEntryPointByName("rayGen", rayGenEntry.writeRef()); + raygenModule->findEntryPointByName("raygen", rayGenEntry.writeRef()); IModule* closestHitModule = session->loadModule("ClosestHit", diagnostics.writeRef()); if (diagnostics) @@ -168,9 +186,17 @@ void GPURenderer::createShaders() Slang::ComPtr closestHitEntry; closestHitModule->findEntryPointByName("closestHit", closestHitEntry.writeRef()); - IComponentType* components[] = {raygenModule, rayGenEntry, closestHitModule, closestHitEntry}; + IModule* missModule = session->loadModule("Miss", diagnostics.writeRef()); + if (diagnostics) + { + std::cout << (const char*)diagnostics->getBufferPointer() << std::endl; + } + Slang::ComPtr missEntry; + missModule->findEntryPointByName("miss", missEntry.writeRef()); + + IComponentType* components[] = {raygenModule, rayGenEntry, closestHitModule, closestHitEntry, missModule, missEntry}; Slang::ComPtr program; - session->createCompositeComponentType(components, 4, program.writeRef()); + session->createCompositeComponentType(components, 6, program.writeRef()); Slang::ComPtr linkedProgram; program->link(linkedProgram.writeRef(), diagnostics.writeRef()); @@ -181,46 +207,28 @@ void GPURenderer::createShaders() Slang::ComPtr closestHitCode; linkedProgram->getEntryPointCode(1, 0, closestHitCode.writeRef(), diagnostics.writeRef()); + Slang::ComPtr missCode; + linkedProgram->getEntryPointCode(2, 0, missCode.writeRef(), diagnostics.writeRef()); + rayGen = ShaderModule(device, vk::ShaderModuleCreateInfo({}, rayGenCode->getBufferSize(), (const uint32_t*)rayGenCode->getBufferPointer())); closestHit = ShaderModule( device, vk::ShaderModuleCreateInfo({}, closestHitCode->getBufferSize(), (const uint32_t*)closestHitCode->getBufferPointer())); - std::vector shaderStages; - std::vector shaderGroups; + miss = ShaderModule(device, vk::ShaderModuleCreateInfo({}, missCode->getBufferSize(), (const uint32_t*)missCode->getBufferPointer())); + + std::vector shaderStages; + std::vector shaderGroups; { - shaderStages.push_back(VkPipelineShaderStageCreateInfo{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .stage = VK_SHADER_STAGE_RAYGEN_BIT_KHR, - .module = *rayGen, - .pName = "rayGen", - .pSpecializationInfo = nullptr, - }); - shaderGroups.push_back(VkRayTracingShaderGroupCreateInfoKHR{ - .sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR, - .pNext = nullptr, - .type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR, - .generalShader = static_cast(shaderStages.size() - 1), - .closestHitShader = VK_SHADER_UNUSED_KHR, - .anyHitShader = VK_SHADER_UNUSED_KHR, - .intersectionShader = VK_SHADER_UNUSED_KHR, - .pShaderGroupCaptureReplayHandle = nullptr, - }); + shaderStages.push_back(vk::PipelineShaderStageCreateInfo({}, vk::ShaderStageFlagBits::eRaygenKHR, rayGen, "main")); + shaderGroups.push_back(vk::RayTracingShaderGroupCreateInfoKHR(vk::RayTracingShaderGroupTypeKHR::eGeneral, shaderStages.size() - 1, + vk::ShaderUnusedKHR, vk::ShaderUnusedKHR, vk::ShaderUnusedKHR)); } { - shaderStages.push_back(VkPipelineShaderStageCreateInfo{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .stage = VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, - .module = *closestHit, - .pName = "closestHit", - .pSpecializationInfo = nullptr, - }); + shaderStages.push_back(vk::PipelineShaderStageCreateInfo({}, vk::ShaderStageFlagBits::eClosestHitKHR, closestHit, "main")); + uint32_t hitIndex = static_cast(shaderStages.size() - 1); uint32_t anyHitIndex = VK_SHADER_UNUSED_KHR; uint32_t intersectionIndex = VK_SHADER_UNUSED_KHR; @@ -250,21 +258,118 @@ void GPURenderer::createShaders() // .pSpecializationInfo = nullptr, // }); // } - shaderGroups.push_back(VkRayTracingShaderGroupCreateInfoKHR{ - .sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR, - .pNext = nullptr, - .type = VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_KHR, - .generalShader = VK_SHADER_UNUSED_KHR, - .closestHitShader = hitIndex, - .anyHitShader = anyHitIndex, - .intersectionShader = intersectionIndex, - .pShaderGroupCaptureReplayHandle = nullptr, - }); + shaderGroups.push_back(vk::RayTracingShaderGroupCreateInfoKHR(vk::RayTracingShaderGroupTypeKHR::eTrianglesHitGroup, vk::ShaderUnusedKHR, + hitIndex, anyHitIndex, intersectionIndex)); } + { + shaderStages.push_back(vk::PipelineShaderStageCreateInfo({}, vk::ShaderStageFlagBits::eMissKHR, miss, "main")); + shaderGroups.push_back(vk::RayTracingShaderGroupCreateInfoKHR(vk::RayTracingShaderGroupTypeKHR::eGeneral, shaderStages.size() - 1, + vk::ShaderUnusedKHR, vk::ShaderUnusedKHR, vk::ShaderUnusedKHR)); + } + pipeline = device.createRayTracingPipelineKHR( + nullptr, nullptr, vk::RayTracingPipelineCreateInfoKHR({}, shaderStages, shaderGroups, 12, nullptr, nullptr, nullptr, pipelineLayout)); + + const uint32_t handleSize = rayTracingProperties.shaderGroupHandleSize; + const uint32_t handleSizeAligned = align(rayTracingProperties.shaderGroupHandleSize, rayTracingProperties.shaderGroupHandleAlignment); + const uint32_t handleAlignment = rayTracingProperties.shaderGroupHandleAlignment; + const uint32_t sbtAlignment = rayTracingProperties.shaderGroupBaseAlignment; + const uint32_t groupCount = static_cast(shaderGroups.size()); + const uint32_t sbtSize = groupCount * handleSizeAligned; + const VkBufferUsageFlags sbtBufferUsage = + VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; + const VmaMemoryUsage sbtMemoryUsage = VMA_MEMORY_USAGE_AUTO; + + uint64_t rayGenStride = handleSize; + uint64_t hitStride = handleSize; + uint64_t missStride = handleSize; + auto rayGenSBTInfo = VkBufferCreateInfo{ + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .size = rayGenStride, + .usage = sbtBufferUsage, + }; + auto rayGenSBTAllocInfo = VmaAllocationCreateInfo{ + .usage = sbtMemoryUsage, + }; + VkBuffer rayGenSBTBuf; + vmaCreateBufferWithAlignment(allocator, &rayGenSBTInfo, &rayGenSBTAllocInfo, sbtAlignment, &rayGenSBTBuf, &rayGenAlloc, nullptr); + rayGenSBT = Buffer(device, rayGenSBTBuf); + rayGenAddr = + vk::StridedDeviceAddressRegionKHR(device.getBufferAddress(vk::BufferDeviceAddressInfo(*rayGenSBT)), rayGenStride, rayGenStride); + + auto closestHitSBTInfo = VkBufferCreateInfo{ + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .size = hitStride, + .usage = sbtBufferUsage, + }; + auto closestHitSBTAllocInfo = VmaAllocationCreateInfo{ + .usage = sbtMemoryUsage, + }; + VkBuffer closestHitSBTBuf; + vmaCreateBufferWithAlignment(allocator, &closestHitSBTInfo, &closestHitSBTAllocInfo, sbtAlignment, &closestHitSBTBuf, &closestHitAlloc, + nullptr); + closestHitSBT = Buffer(device, closestHitSBTBuf); + closestHitAddr = + vk::StridedDeviceAddressRegionKHR(device.getBufferAddress(vk::BufferDeviceAddressInfo(*closestHitSBT)), hitStride, hitStride); + + auto missSBTInfo = VkBufferCreateInfo{ + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .size = missStride, + .usage = sbtBufferUsage, + }; + auto missSBTAllocInfo = VmaAllocationCreateInfo{ + .usage = sbtMemoryUsage, + }; + VkBuffer missSBTBuf; + vmaCreateBufferWithAlignment(allocator, &missSBTInfo, &missSBTAllocInfo, sbtAlignment, &missSBTBuf, &missAlloc, nullptr); + missSBT = Buffer(device, missSBTBuf); + missAddr = vk::StridedDeviceAddressRegionKHR(device.getBufferAddress(vk::BufferDeviceAddressInfo(*missSBT)), missStride, missStride); + + std::vector sbt = pipeline.getRayTracingShaderGroupHandlesKHR(0, shaderGroups.size(), sbtSize); + + uploadToGPU(rayGenSBT, sbt.data(), rayGenStride); + uploadToGPU(closestHitSBT, sbt.data() + handleSize, handleSize); + uploadToGPU(missSBT, sbt.data() + handleSize * 2, handleSize); +} + +void GPURenderer::uploadToGPU(Buffer& buffer, void* data, size_t size) +{ + VkBufferCreateInfo stagingBufInfo = { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .size = size, + .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + }; + VmaAllocationCreateInfo stagingAllocInfo = { + .flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT, + .usage = VMA_MEMORY_USAGE_AUTO, + }; + VkBuffer stagingBuf; + VmaAllocation stagingAllocation; + vmaCreateBuffer(allocator, &stagingBufInfo, &stagingAllocInfo, &stagingBuf, &stagingAllocation, nullptr); + Buffer stagingBuffer = Buffer(device, stagingBuf); + + vmaCopyMemoryToAllocation(allocator, data, stagingAllocation, 0, size); + CommandBuffer copyCmd = + std::move(device.allocateCommandBuffers(vk::CommandBufferAllocateInfo(cmdPool, vk::CommandBufferLevel::ePrimary, 1)).front()); + copyCmd.begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit)); + copyCmd.copyBuffer(stagingBuffer, buffer, vk::BufferCopy(0, 0, size)); + copyCmd.end(); + queue.submit(vk::SubmitInfo({}, {}, *copyCmd, {})); + device.waitIdle(); } void GPURenderer::render(Camera cam, RenderParameter param) { + for (uint32_t samp = 0; samp < param.numSamples; ++samp) + { + semaphores.push_back(device.createSemaphore(vk::SemaphoreCreateInfo())); + fences.push_back(device.createFence(vk::FenceCreateInfo())); + } // camera { VkBufferCreateInfo bufferInfo = { @@ -277,7 +382,9 @@ void GPURenderer::render(Camera cam, RenderParameter param) .usage = VMA_MEMORY_USAGE_AUTO, }; - vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &cameraBuffer, &cameraAllocation, nullptr); + VkBuffer camBuf; + vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &camBuf, &cameraAllocation, nullptr); + cameraBuffer = Buffer(device, camBuf); GPUCamera gpuCam = { .cameraPosition = cam.position, .f = cam.f, @@ -288,7 +395,7 @@ void GPURenderer::render(Camera cam, RenderParameter param) .A = cam.A, .ka = 0, }; - vmaCopyMemoryToAllocation(allocator, &gpuCam, cameraAllocation, 0, sizeof(GPUCamera)); + uploadToGPU(cameraBuffer, &gpuCam, sizeof(GPUCamera)); } // radiance accumulator { @@ -317,7 +424,8 @@ void GPURenderer::render(Camera cam, RenderParameter param) VkImage radianceImg; vmaCreateImage(allocator, &imageInfo, &allocCreateInfo, &radianceImg, &radianceAllocation, nullptr); radianceAccumulator = Image(device, radianceImg); - radianceView = device.createImageView(vk::ImageViewCreateInfo({}, *radianceAccumulator, vk::ImageViewType::e2D, vk::Format::eR32G32B32A32Sfloat)); + radianceView = + device.createImageView(vk::ImageViewCreateInfo({}, *radianceAccumulator, vk::ImageViewType::e2D, vk::Format::eR32G32B32A32Sfloat)); } // image { @@ -349,7 +457,8 @@ void GPURenderer::render(Camera cam, RenderParameter param) radianceView = device.createImageView(vk::ImageViewCreateInfo({}, *image, vk::ImageViewType::e2D, vk::Format::eR32G32B32A32Sfloat)); } - DescriptorSet descriptorSet = std::move(device.allocateDescriptorSets(vk::DescriptorSetAllocateInfo(*descriptorPool, *descriptorLayout)).front()); + DescriptorSet descriptorSet = + std::move(device.allocateDescriptorSets(vk::DescriptorSetAllocateInfo(*descriptorPool, *descriptorLayout)).front()); std::vector writes; // have to use lists so the pointers arent invalidated by push std::list buffers; @@ -358,61 +467,66 @@ void GPURenderer::render(Camera cam, RenderParameter param) uint32_t bindingCounter = 0; { buffers.push_back(vk::DescriptorBufferInfo(cameraBuffer)); - writes.push_back(vk::WriteDescriptorSet(*descriptorSet, bindingCounter++, 0, 1, vk::DescriptorType::eUniformBuffer, nullptr, &buffers.back(), nullptr)); + writes.push_back(vk::WriteDescriptorSet(*descriptorSet, bindingCounter++, 0, 1, vk::DescriptorType::eUniformBuffer, nullptr, + &buffers.back(), nullptr)); } { - accel.push_back(vk::WriteDescriptorSetAccelerationStructureKHR(1, scene->accelerationStructure)); - writes.push_back(vk::WriteDescriptorSet(*descriptorSet, bindingCounter++, 0, 1, vk::DescriptorType::eAccelerationStructureKHR, nullptr, nullptr, nullptr, &accel.back())); + accel.push_back(vk::WriteDescriptorSetAccelerationStructureKHR(*((GPUScene*)scene.get())->accelerationStructure)); + writes.push_back(vk::WriteDescriptorSet(*descriptorSet, bindingCounter++, 0, 1, vk::DescriptorType::eAccelerationStructureKHR, nullptr, + nullptr, nullptr, &accel.back())); } { images.push_back(vk::DescriptorImageInfo({}, radianceView, vk::ImageLayout::eGeneral)); - writes.push_back(vk::WriteDescriptorSet(*descriptorSet, bindingCounter++, 0, 1, vk::DescriptorType::eStorageImage, &images.back(), nullptr, nullptr)); + writes.push_back(vk::WriteDescriptorSet(*descriptorSet, bindingCounter++, 0, 1, vk::DescriptorType::eStorageImage, &images.back(), + nullptr, nullptr)); } { images.push_back(vk::DescriptorImageInfo({}, imageView, vk::ImageLayout::eGeneral)); - writes.push_back(vk::WriteDescriptorSet(*descriptorSet, bindingCounter++, 0, 1, vk::DescriptorType::eStorageImage, &images.back(), nullptr, nullptr)); + writes.push_back(vk::WriteDescriptorSet(*descriptorSet, bindingCounter++, 0, 1, vk::DescriptorType::eStorageImage, &images.back(), + nullptr, nullptr)); } { - buffers.push_back(vk::DescriptorBufferInfo(scene->modelBuffer)); - writes.push_back(vk::WriteDescriptorSet(*descriptorSet, bindingCounter++, 0, 1, vk::DescriptorType::eStorageBuffer, nullptr, &buffers.back(), nullptr)); + buffers.push_back(vk::DescriptorBufferInfo(((GPUScene*)scene.get())->modelBuffer)); + writes.push_back(vk::WriteDescriptorSet(*descriptorSet, bindingCounter++, 0, 1, vk::DescriptorType::eStorageBuffer, nullptr, + &buffers.back(), nullptr)); } { - buffers.push_back(vk::DescriptorBufferInfo(scene->materialBuffer)); - writes.push_back(vk::WriteDescriptorSet(*descriptorSet, bindingCounter++, 0, 1, vk::DescriptorType::eStorageBuffer, nullptr, &buffers.back(), nullptr)); + buffers.push_back(vk::DescriptorBufferInfo(((GPUScene*)scene.get())->materialBuffer)); + writes.push_back(vk::WriteDescriptorSet(*descriptorSet, bindingCounter++, 0, 1, vk::DescriptorType::eStorageBuffer, nullptr, + &buffers.back(), nullptr)); } { - buffers.push_back(vk::DescriptorBufferInfo(scene->positionsBuffer)); - writes.push_back(vk::WriteDescriptorSet(*descriptorSet, bindingCounter++, 0, 1, vk::DescriptorType::eStorageBuffer, nullptr, &buffers.back(), nullptr)); + buffers.push_back(vk::DescriptorBufferInfo(((GPUScene*)scene.get())->positionBuffer)); + writes.push_back(vk::WriteDescriptorSet(*descriptorSet, bindingCounter++, 0, 1, vk::DescriptorType::eStorageBuffer, nullptr, + &buffers.back(), nullptr)); } { - buffers.push_back(vk::DescriptorBufferInfo(scene->texCoordsBuffer)); - writes.push_back(vk::WriteDescriptorSet(*descriptorSet, bindingCounter++, 0, 1, vk::DescriptorType::eStorageBuffer, nullptr, &buffers.back(), nullptr)); + buffers.push_back(vk::DescriptorBufferInfo(((GPUScene*)scene.get())->texCoordsBuffer)); + writes.push_back(vk::WriteDescriptorSet(*descriptorSet, bindingCounter++, 0, 1, vk::DescriptorType::eStorageBuffer, nullptr, + &buffers.back(), nullptr)); } { - buffers.push_back(vk::DescriptorBufferInfo(scene->normalsBuffer)); - writes.push_back(vk::WriteDescriptorSet(*descriptorSet, bindingCounter++, 0, 1, vk::DescriptorType::eStorageBuffer, nullptr, &buffers.back(), nullptr)); + buffers.push_back(vk::DescriptorBufferInfo(((GPUScene*)scene.get())->normalsBuffer)); + writes.push_back(vk::WriteDescriptorSet(*descriptorSet, bindingCounter++, 0, 1, vk::DescriptorType::eStorageBuffer, nullptr, + &buffers.back(), nullptr)); } { - buffers.push_back(vk::DescriptorBufferInfo(scene->directionalLightsBuffer)); - writes.push_back(vk::WriteDescriptorSet(*descriptorSet, bindingCounter++, 0, 1, vk::DescriptorType::eStorageBuffer, nullptr, &buffers.back(), nullptr)); + buffers.push_back(vk::DescriptorBufferInfo(((GPUScene*)scene.get())->directionalLightBuffer)); + writes.push_back(vk::WriteDescriptorSet(*descriptorSet, bindingCounter++, 0, 1, vk::DescriptorType::eStorageBuffer, nullptr, + &buffers.back(), nullptr)); } { - buffers.push_back(vk::DescriptorBufferInfo(scene->pointLightsBuffer)); - writes.push_back(vk::WriteDescriptorSet(*descriptorSet, bindingCounter++, 0, 1, vk::DescriptorType::eStorageBuffer, nullptr, &buffers.back(), nullptr)); + buffers.push_back(vk::DescriptorBufferInfo(((GPUScene*)scene.get())->pointLightBuffer)); + writes.push_back(vk::WriteDescriptorSet(*descriptorSet, bindingCounter++, 0, 1, vk::DescriptorType::eStorageBuffer, nullptr, + &buffers.back(), nullptr)); } { - buffers.push_back(vk::DescriptorBufferInfo(scene->indexBuffer)); - writes.push_back(vk::WriteDescriptorSet(*descriptorSet, bindingCounter++, 0, 1, vk::DescriptorType::eStorageBuffer, nullptr, &buffers.back(), nullptr)); + buffers.push_back(vk::DescriptorBufferInfo(((GPUScene*)scene.get())->indexBuffer)); + writes.push_back(vk::WriteDescriptorSet(*descriptorSet, bindingCounter++, 0, 1, vk::DescriptorType::eStorageBuffer, nullptr, + &buffers.back(), nullptr)); } device.updateDescriptorSets(writes, {}); - - semaphores.resize(param.numSamples); - fences.resize(param.numSamples); - for (uint32_t samp = 0; samp < param.numSamples; ++samp) - { - semaphores[samp] = device.createSemaphore(vk::SemaphoreCreateInfo()); - fences[samp] = device.createFence(vk::FenceCreateInfo()); - } + // allocate a CommandBuffer from the CommandPool vk::CommandBufferAllocateInfo commandBufferAllocateInfo(*cmdPool, vk::CommandBufferLevel::ePrimary, param.numSamples); cmdBuffers = CommandBuffers(device, commandBufferAllocateInfo); @@ -421,20 +535,29 @@ void GPURenderer::render(Camera cam, RenderParameter param) auto& cmd = cmdBuffers[samp]; cmd.begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit)); cmd.bindPipeline(vk::PipelineBindPoint::eRayTracingKHR, *pipeline); - cmd.bindDescriptorSets(vk::PipelineBindPoint::eRayTracingKHR, pipelineLayout, 0, descriptorSet, {}); - cmd.traceRays(param.width, param.height, 1); + cmd.bindDescriptorSets(vk::PipelineBindPoint::eRayTracingKHR, pipelineLayout, 0, *descriptorSet, {}); + std::vector sampleParams = {SampleParams{ + .pass = samp, + .samplesPerPixel = param.numSamples, + .numDirectionalLights = (uint32_t)scene->directionalLights.size(), + .numPointLights = (uint32_t)scene->pointLights.size(), + }}; + cmd.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eRaygenKHR | vk::ShaderStageFlagBits::eClosestHitKHR, 0, + sampleParams); + cmd.traceRaysKHR(rayGenAddr, closestHitAddr, missAddr, {}, param.width, param.height, 1); cmd.end(); if (samp == 0) { - queue.submit(vk::SubmitInfo({}, cmd, semaphores[samp]), fences[samp]); + queue.submit(vk::SubmitInfo({}, {}, *cmd, *semaphores[samp]), *fences[samp]); } else { - queue.submit(vk::SubmitInfo(semaphores[samp-1], vk::PipelineStageFlagBits::eRayTracingShaderKHR, cmd, semaphores[samp]), fences[samp]); + vk::PipelineStageFlags dstWaitMask = vk::PipelineStageFlagBits::eRayTracingShaderKHR; + queue.submit(vk::SubmitInfo(*semaphores[samp - 1], dstWaitMask, *cmd, *semaphores[samp]), *fences[samp]); } } for (uint32_t samp = 0; samp < param.numSamples; ++samp) { - device.waitForFences(fences[samp], true, 1000000); + assert(device.waitForFences(*fences[samp], true, 1000000) == vk::Result::eSuccess); } } \ No newline at end of file diff --git a/src/gpu/GPURenderer.h b/src/gpu/GPURenderer.h index 20d355b..bd4a027 100644 --- a/src/gpu/GPURenderer.h +++ b/src/gpu/GPURenderer.h @@ -12,6 +12,7 @@ struct GPURenderer : public Renderer public: GPURenderer(); virtual ~GPURenderer(); + virtual void render(Camera cam, RenderParameter param) override; private: struct GPUCamera @@ -25,47 +26,68 @@ private: float A; float ka; }; + + struct SampleParams + { + uint32_t pass; + uint32_t samplesPerPixel; + uint32_t numDirectionalLights; + uint32_t numPointLights; + }; void createDevice(); void createCommands(); void createDescriptors(); - void createShaders(); - - std::unique_ptr scene; + void createPipeline(); Context context; - Instance instance; - PhysicalDevice physicalDevice; - Device device; - Queue queue; - VmaAllocator allocator; + Instance instance = nullptr; + PhysicalDevice physicalDevice = nullptr; + Device device = nullptr; + Queue queue = nullptr; + VmaAllocator allocator = nullptr; - uint32_t computeQueueFamily; - CommandPool cmdPool; - CommandBuffers cmdBuffers; + vk::PhysicalDeviceAccelerationStructurePropertiesKHR accelerationProperties = {}; + vk::PhysicalDeviceRayTracingPipelinePropertiesKHR rayTracingProperties = {}; + + uint32_t computeQueueFamily = 0; + CommandPool cmdPool = nullptr; + CommandBuffers cmdBuffers = nullptr; std::vector semaphores; std::vector fences; - DescriptorSetLayout descriptorLayout; - DescriptorSet descriptorSet; - DescriptorPool descriptorPool; - PipelineLayout pipelineLayout; + DescriptorSetLayout descriptorLayout = nullptr; + DescriptorSet descriptorSet = nullptr; + DescriptorPool descriptorPool = nullptr; + PipelineLayout pipelineLayout = nullptr; - ShaderModule rayGen; - ShaderModule closestHit; - ShaderModule miss; + ShaderModule rayGen = nullptr; + ShaderModule closestHit = nullptr; + ShaderModule miss = nullptr; - Pipeline pipeline; + Pipeline pipeline = nullptr; - Buffer cameraBuffer; + Buffer rayGenSBT = nullptr; + vk::StridedDeviceAddressRegionKHR rayGenAddr; + VmaAllocation rayGenAlloc; + + Buffer closestHitSBT = nullptr; + vk::StridedDeviceAddressRegionKHR closestHitAddr; + VmaAllocation closestHitAlloc; + + Buffer missSBT = nullptr; + vk::StridedDeviceAddressRegionKHR missAddr; + VmaAllocation missAlloc; + + Buffer cameraBuffer = nullptr; VmaAllocation cameraAllocation; - Image radianceAccumulator; - ImageView radianceView; + Image radianceAccumulator = nullptr; + ImageView radianceView = nullptr; VmaAllocation radianceAllocation; - Image image; - ImageView imageView; + Image image = nullptr; + ImageView imageView = nullptr; VmaAllocation imageAllocation; - virtual void render(Camera cam, RenderParameter param); + void uploadToGPU(Buffer& buffer, void* data, size_t size); }; \ No newline at end of file diff --git a/src/gpu/GPUScene.cpp b/src/gpu/GPUScene.cpp index 7cd6a4a..acca3e3 100644 --- a/src/gpu/GPUScene.cpp +++ b/src/gpu/GPUScene.cpp @@ -1,11 +1,14 @@ #include "GPUScene.h" +GPUScene::GPUScene(Device& device, VmaAllocator& allocator, CommandPool& cmdPool, Queue& queue) + : device(device), allocator(allocator), cmdPool(cmdPool), queue(queue) +{ +} + GPUScene::~GPUScene() {} -void GPUScene::generate() +void GPUScene::createRayTracingHierarchy() { - populateGeometryPools(); - // upload geometry to gpu createStorageBuffer(modelBuffer, modelAllocation, refs.data(), refs.size() * sizeof(ModelReference)); // createStorageBuffer(materialBuffer, materialAllocation, refs.data(), refs.size() * sizeof(ModelReference)); @@ -17,108 +20,193 @@ void GPUScene::generate() createStorageBuffer(pointLightBuffer, pointLightAllocation, pointLights.data(), pointLights.size() * sizeof(PointLight)); createStorageBuffer(indexBuffer, indexAllocation, indicesPool.data(), indicesPool.size() * sizeof(glm::uvec3)); - VkBufferDeviceAddressInfo addrInfo = { - .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, - .buffer = positionBuffer, - }; - VkDeviceAddress vertexBufferAddr = vkGetBufferDeviceAddress(*device, &addrInfo); - addrInfo.buffer = indexBuffer; - VkDeviceAddress indexBufferAddr = vkGetBufferDeviceAddress(*device, &addrInfo); + vk::DeviceAddress vertexBufferAddr = device.getBufferAddress(vk::BufferDeviceAddressInfo(positionBuffer)); + vk::DeviceAddress indexBufferAddr = device.getBufferAddress(vk::BufferDeviceAddressInfo(indexBuffer)); - std::vector geometries(models.size()); - std::vector buildGeometries(models.size()); - std::vector buildSizes(models.size()); - std::vector scratchBuffers(models.size()); - std::vector scratchAllocations(models.size()); - std::vector buildRanges(models.size()); - std::vector buildRangePointers(models.size()); - blas.resize(models.size()); - for (uint32_t i = 0; i < models.size(); ++i) + std::vector instances(models.size()); { - vk::DeviceOrHostAddressConstKHR vertexDataAddress = (vertexBufferAddr + refs[i].positionOffset * sizeof(glm::vec3)); - vk::DeviceOrHostAddressConstKHR indexDataAddress = (indexBufferAddr + refs[i].indicesOffset + sizeof(glm::uvec3)); + std::vector geometries(models.size()); + std::vector buildGeometries(models.size()); + std::vector buildSizes(models.size()); + std::vector scratchBuffers(models.size()); + std::vector scratchAllocations(models.size()); + std::vector buildRanges(models.size()); + std::vector buildRangePointers(models.size()); + blas.resize(models.size()); + for (uint32_t i = 0; i < models.size(); ++i) + { + vk::DeviceOrHostAddressConstKHR vertexDataAddress = (vertexBufferAddr + refs[i].positionOffset * sizeof(glm::vec3)); + vk::DeviceOrHostAddressConstKHR indexDataAddress = (indexBufferAddr + refs[i].indicesOffset + sizeof(glm::uvec3)); - geometries[i] = vk::AccelerationStructureGeometryKHR( - vk::GeometryTypeKHR::eTriangles, - vk::AccelerationStructureGeometryTrianglesDataKHR(vk::Format::eR32G32B32Sfloat, vertexDataAddress, sizeof(glm::vec3), - (uint32_t)refs[i].numPositions, vk::IndexType::eUint32, indexDataAddress), - vk::GeometryFlagBitsKHR::eOpaque); + geometries[i] = vk::AccelerationStructureGeometryKHR( + vk::GeometryTypeKHR::eTriangles, + vk::AccelerationStructureGeometryTrianglesDataKHR(vk::Format::eR32G32B32Sfloat, vertexDataAddress, sizeof(glm::vec3), + (uint32_t)refs[i].numPositions, vk::IndexType::eUint32, indexDataAddress), + vk::GeometryFlagBitsKHR::eOpaque); - buildGeometries[i] = vk::AccelerationStructureBuildGeometryInfoKHR( - vk::AccelerationStructureTypeKHR::eTopLevel, vk::BuildAccelerationStructureFlagBitsKHR::ePreferFastTrace, - vk::BuildAccelerationStructureModeKHR::eBuild, {}, {}, 1, &geometries[i], nullptr); + buildGeometries[i] = vk::AccelerationStructureBuildGeometryInfoKHR( + vk::AccelerationStructureTypeKHR::eBottomLevel, vk::BuildAccelerationStructureFlagBitsKHR::ePreferFastTrace, + vk::BuildAccelerationStructureModeKHR::eBuild, {}, {}, 1, &geometries[i], nullptr); - buildSizes[i] = { - .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR, - .pNext = nullptr, - }; - const uint32_t primitiveCount = refs[i].numIndices / 3; - buildSizes[i] = - device.getAccelerationStructureBuildSizesKHR(vk::AccelerationStructureBuildTypeKHR::eDevice, buildGeometries[i], primitiveCount); + buildSizes[i] = { + .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR, + .pNext = nullptr, + }; + const uint32_t primitiveCount = refs[i].numIndices / 3; + buildSizes[i] = + device.getAccelerationStructureBuildSizesKHR(vk::AccelerationStructureBuildTypeKHR::eDevice, buildGeometries[i], primitiveCount); - VkBufferCreateInfo bufferInfo = { - .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .size = buildSizes[i].accelerationStructureSize, - .usage = VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, - }; - VmaAllocationCreateInfo bufferAllocInfo = { - .usage = VMA_MEMORY_USAGE_AUTO, - }; - vmaCreateBuffer(allocator, &bufferInfo, &bufferAllocInfo, &blas[i].buffer, &blas[i].alloc, nullptr); + VkBufferCreateInfo bufferInfo = { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .size = buildSizes[i].accelerationStructureSize, + .usage = VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + }; + VmaAllocationCreateInfo bufferAllocInfo = { + .usage = VMA_MEMORY_USAGE_AUTO, + }; + VkBuffer buf; + vmaCreateBuffer(allocator, &bufferInfo, &bufferAllocInfo, &buf, &blas[i].alloc, nullptr); + blas[i].buffer = Buffer(device, buf); - vk::AccelerationStructureCreateInfoKHR blasInfo({}, blas[i].buffer, 0, buildSizes[i].accelerationStructureSize, - vk::AccelerationStructureTypeKHR::eBottomLevel); - blas[i].handle = device.createAccelerationStructureKHR(blasInfo); + vk::AccelerationStructureCreateInfoKHR blasInfo({}, blas[i].buffer, 0, buildSizes[i].accelerationStructureSize, + vk::AccelerationStructureTypeKHR::eBottomLevel); + blas[i].handle = device.createAccelerationStructureKHR(blasInfo); - VkBufferCreateInfo scratchInfo = { - .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .size = buildSizes[i].buildScratchSize, - .usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, - }; - VmaAllocationCreateInfo scratchAllocInfo = { - .usage = VMA_MEMORY_USAGE_AUTO, - }; - vmaCreateBufferWithAlignment(allocator, &scratchInfo, &scratchAllocInfo, 16, &scratchBuffers[i], &scratchAllocations[i], nullptr); - addrInfo.buffer = scratchBuffers[i]; - VkDeviceAddress scratchAddr = vkGetBufferDeviceAddress(*device, &addrInfo); - buildGeometries[i].dstAccelerationStructure = blas[i].handle; - buildGeometries[i].scratchData.deviceAddress = scratchAddr; + VkBufferCreateInfo scratchInfo = { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .size = buildSizes[i].buildScratchSize, + .usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + }; + VmaAllocationCreateInfo scratchAllocInfo = { + .usage = VMA_MEMORY_USAGE_AUTO, + }; + vmaCreateBufferWithAlignment(allocator, &scratchInfo, &scratchAllocInfo, 16, &scratchBuffers[i], &scratchAllocations[i], nullptr); + vk::DeviceAddress scratchAddr = device.getBufferAddress(vk::BufferDeviceAddressInfo(scratchBuffers[i])); + buildGeometries[i].dstAccelerationStructure = blas[i].handle; + buildGeometries[i].scratchData.deviceAddress = scratchAddr; - buildRanges[i] = VkAccelerationStructureBuildRangeInfoKHR{ - .primitiveCount = primitiveCount, - .primitiveOffset = 0, - .firstVertex = 0, - .transformOffset = 0, - }; - buildRangePointers[i] = &buildRanges[i]; + buildRanges[i] = VkAccelerationStructureBuildRangeInfoKHR{ + .primitiveCount = primitiveCount, + .primitiveOffset = 0, + .firstVertex = 0, + .transformOffset = 0, + }; + buildRangePointers[i] = &buildRanges[i]; + vk::DeviceAddress blasAddr = device.getBufferAddress(vk::BufferDeviceAddressInfo(blas[i].buffer)); + instances[i] = vk::AccelerationStructureInstanceKHR({}, i, 0xff, 0, {}, blasAddr); + } + vk::CommandBufferAllocateInfo commandBufferAllocateInfo(*cmdPool, vk::CommandBufferLevel::ePrimary, 10); + CommandBuffer cmdBuffer = std::move(CommandBuffers(device, commandBufferAllocateInfo).front()); + cmdBuffer.begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit)); + cmdBuffer.buildAccelerationStructuresKHR(buildGeometries, buildRangePointers); + cmdBuffer.end(); + vk::SubmitInfo submitInfo; + queue.submit(submitInfo); + device.waitIdle(); } + createStorageBuffer(instanceBuffer, instanceAllocation, instances.data(), instances.size()); + vk::DeviceAddress instancesAddress = device.getBufferAddress(vk::BufferDeviceAddressInfo(instanceBuffer)); + vk::AccelerationStructureGeometryKHR geometry(vk::GeometryTypeKHR::eInstances, + vk::AccelerationStructureGeometryInstancesDataKHR(false, {instancesAddress}), + vk::GeometryFlagBitsKHR::eOpaque); + vk::AccelerationStructureBuildGeometryInfoKHR structureBuildGeometry(vk::AccelerationStructureTypeKHR::eTopLevel, + vk::BuildAccelerationStructureFlagBitsKHR::ePreferFastTrace, + vk::BuildAccelerationStructureModeKHR::eBuild, {}, {}, geometry); + + const uint32_t primitiveCount = instances.size(); + auto buildSizes = + device.getAccelerationStructureBuildSizesKHR(vk::AccelerationStructureBuildTypeKHR::eDevice, structureBuildGeometry, primitiveCount); + + VkBuffer buffer; + auto tlasInfo = VkBufferCreateInfo{ + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .size = buildSizes.accelerationStructureSize, + .usage = VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + }; + auto tlasAlloc = VmaAllocationCreateInfo{ + .usage = VMA_MEMORY_USAGE_AUTO, + }; + vmaCreateBuffer(allocator, &tlasInfo, &tlasAlloc, &buffer, &accelerationAllocation, nullptr); + accelerationBuffer = Buffer(device, buffer); + + accelerationStructure = device.createAccelerationStructureKHR(vk::AccelerationStructureCreateInfoKHR( + {}, accelerationBuffer, 0, buildSizes.accelerationStructureSize, vk::AccelerationStructureTypeKHR::eTopLevel)); + + auto scratchInfo = VkBufferCreateInfo{ + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .size = buildSizes.buildScratchSize, + .usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + }; + auto scratchAllocInfo = VmaAllocationCreateInfo{ + .usage = VMA_MEMORY_USAGE_AUTO, + }; + VkBuffer scratchBuf; + VmaAllocation scratchAlloc; + vmaCreateBufferWithAlignment(allocator, &scratchInfo, &scratchAllocInfo, 64, &scratchBuf, &scratchAlloc, nullptr); + Buffer scratchBuffer = Buffer(device, scratchBuf); + + vk::DeviceAddress scratchAddr = device.getBufferAddress(vk::BufferDeviceAddressInfo(scratchBuffer)); + vk::AccelerationStructureBuildGeometryInfoKHR buildGeometry( + vk::AccelerationStructureTypeKHR::eTopLevel, vk::BuildAccelerationStructureFlagBitsKHR::ePreferFastTrace, + vk::BuildAccelerationStructureModeKHR::eBuild, {}, accelerationStructure, geometry, {}, {scratchAddr}); + + vk::AccelerationStructureBuildRangeInfoKHR buildRange(primitiveCount, 0, 0, 0); + vk::CommandBufferAllocateInfo commandBufferAllocateInfo(*cmdPool, vk::CommandBufferLevel::ePrimary, 10); CommandBuffer cmdBuffer = std::move(CommandBuffers(device, commandBufferAllocateInfo).front()); - vk::FenceCreateInfo fenceCreateInfo; - Fence fence = Fence(device, fenceCreateInfo); cmdBuffer.begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit)); - cmdBuffer.buildAccelerationStructuresKHR(buildGeometries, buildRangePointers); + cmdBuffer.buildAccelerationStructuresKHR(buildGeometry, {&buildRange}); cmdBuffer.end(); vk::SubmitInfo submitInfo; - queue.submit(submitInfo, *fence); - assert(device.waitForFences({*fence}, true, 1000000) == vk::Result::eSuccess); + queue.submit(submitInfo); + device.waitIdle(); } -void GPUScene::createStorageBuffer(VkBuffer& buffer, VmaAllocation& alloc, void* data, size_t size) +void GPUScene::createStorageBuffer(Buffer& buffer, VmaAllocation& alloc, void* data, size_t size) { + if (size == 0) + return; VkBufferCreateInfo bufferCreateInfo = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO}; bufferCreateInfo.size = size; - bufferCreateInfo.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + bufferCreateInfo.usage = VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; VmaAllocationCreateInfo allocCreateInfo = {}; allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; - vmaCreateBuffer(allocator, &bufferCreateInfo, &allocCreateInfo, &buffer, &alloc, nullptr); + VkBuffer buf; + vmaCreateBuffer(allocator, &bufferCreateInfo, &allocCreateInfo, &buf, &alloc, nullptr); + buffer = Buffer(device, buf); - vmaCopyMemoryToAllocation(allocator, data, alloc, 0, size); + VkBufferCreateInfo stagingBufInfo = { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .size = size, + .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + }; + VmaAllocationCreateInfo stagingAllocInfo = { + .flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT, + .usage = VMA_MEMORY_USAGE_AUTO, + }; + VkBuffer stagingBuf; + VmaAllocation stagingAllocation; + vmaCreateBuffer(allocator, &stagingBufInfo, &stagingAllocInfo, &stagingBuf, &stagingAllocation, nullptr); + Buffer stagingBuffer = Buffer(device, stagingBuf); + + vmaCopyMemoryToAllocation(allocator, data, stagingAllocation, 0, size); + CommandBuffer copyCmd = + std::move(device.allocateCommandBuffers(vk::CommandBufferAllocateInfo(cmdPool, vk::CommandBufferLevel::ePrimary, 1)).front()); + copyCmd.begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit)); + copyCmd.copyBuffer(stagingBuffer, buffer, vk::BufferCopy(0, 0, size)); + copyCmd.end(); + queue.submit(vk::SubmitInfo({}, {}, *copyCmd, {})); + device.waitIdle(); } diff --git a/src/gpu/GPUScene.h b/src/gpu/GPUScene.h index ea03328..4009ebd 100644 --- a/src/gpu/GPUScene.h +++ b/src/gpu/GPUScene.h @@ -9,12 +9,12 @@ using namespace vk::raii; class GPUScene : public Scene { public: - GPUScene(Device& device, VmaAllocator& allocator, CommandPool& cmdPool); + GPUScene(Device& device, VmaAllocator& allocator, CommandPool& cmdPool, Queue& queue); virtual ~GPUScene(); - virtual void generate() override; + virtual void createRayTracingHierarchy() override; private: - void createStorageBuffer(VkBuffer& buffer, VmaAllocation& alloc, void* data, size_t size); + void createStorageBuffer(Buffer& buffer, VmaAllocation& alloc, void* data, size_t size); Device& device; VmaAllocator& allocator; @@ -24,38 +24,41 @@ private: // bottom level acceleration structure struct BLAS { - vk::AccelerationStructureKHR handle; - VkBuffer buffer; - VmaAllocation alloc; + vk::AccelerationStructureKHR handle = nullptr; + vk::Buffer buffer = nullptr; + VmaAllocation alloc = nullptr; }; - AccelerationStructureKHR accelerationStructure; - VmaAllocation accelerationAllocation; + AccelerationStructureKHR accelerationStructure = nullptr; + Buffer accelerationBuffer = nullptr; + VmaAllocation accelerationAllocation = nullptr; + Buffer instanceBuffer = nullptr; + VmaAllocation instanceAllocation = nullptr; std::vector blas; - VkBuffer modelBuffer; + Buffer modelBuffer = nullptr; VmaAllocation modelAllocation; - VkBuffer materialBuffer; + Buffer materialBuffer = nullptr; VmaAllocation materialAllocation; - VkBuffer positionBuffer; + Buffer positionBuffer = nullptr; VmaAllocation positionAllocation; - VkBuffer texCoordsBuffer; + Buffer texCoordsBuffer = nullptr; VmaAllocation texCoordsAllocation; - VkBuffer normalsBuffer; + Buffer normalsBuffer = nullptr; VmaAllocation normalsAllocation; - VkBuffer directionalLightBuffer; + Buffer directionalLightBuffer = nullptr; VmaAllocation directionalLightAllocation; - VkBuffer pointLightBuffer; + Buffer pointLightBuffer = nullptr; VmaAllocation pointLightAllocation; - VkBuffer indexBuffer; + Buffer indexBuffer = nullptr; VmaAllocation indexAllocation; friend class GPURenderer; }; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index ff74771..b35478e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,3 +1,4 @@ +#include "gpu/GPURenderer.h" #include "scene/Renderer.h" #include "util/ModelLoader.h" #include "window/Window.h" @@ -6,19 +7,19 @@ int main() { - Renderer scene; + std::unique_ptr scene = std::make_unique(); Window window(1920, 1080); Camera camera = Camera{ - .position = glm::vec3(-30, 5, 5), + .position = glm::vec3(5, 1, 2), .target = glm::vec3(0, 0, 0), - .S_O = 40, + .S_O = 6, }; RenderParameter render = RenderParameter{ .width = 1920, .height = 1080, .numSamples = 10000, }; - scene.startRender(camera, render); + scene->startRender(camera, render); while (true) { @@ -30,17 +31,17 @@ int main() ImGui::InputFloat("Aperture", &camera.A); ImGui::InputFloat("S_O", &camera.S_O); ImGui::Text("Render Parameters"); - ImGui::InputInt2("Dimensions", &render.width); - ImGui::InputInt("Samples", &render.numSamples); + ImGui::InputInt2("Dimensions", (int*)&render.width); + ImGui::InputInt("Samples", (int*)&render.numSamples); if (ImGui::Button("Render")) { - scene.startRender(camera, render); + scene->startRender(camera, render); } ImGui::Text("Render Stats"); - ImGui::Text("Last Sample Time: %.3f ms", scene.getLastSampleTime()); - ImGui::Text("Average Sample Time: %.3f ms", scene.getAverageSampleTime()); - ImGui::PlotLines("Sample Times", scene.getSampleTimes().data(), scene.getSampleTimes().size(), 0, 0, FLT_MAX, FLT_MAX, ImVec2(0, 40)); - window.update(scene.getImage()); + ImGui::Text("Last Sample Time: %.3f ms", scene->getLastSampleTime()); + ImGui::Text("Average Sample Time: %.3f ms", scene->getAverageSampleTime()); + ImGui::PlotLines("Sample Times", scene->getSampleTimes().data(), scene->getSampleTimes().size(), 0, 0, FLT_MAX, FLT_MAX, ImVec2(0, 40)); + window.update(scene->getImage()); } return 0; } \ No newline at end of file diff --git a/src/scene/Renderer.cpp b/src/scene/Renderer.cpp index 2812945..0d8438c 100644 --- a/src/scene/Renderer.cpp +++ b/src/scene/Renderer.cpp @@ -1,4 +1,5 @@ #include "Renderer.h" +#include "gpu/GPUScene.h" #include "util/ModelLoader.h" #include #include @@ -6,14 +7,16 @@ Renderer::Renderer() { - bvh.addDirectionalLight(DirectionalLight{ + scene = std::make_unique(); + + scene->addDirectionalLight(DirectionalLight{ .direction = glm::normalize(glm::vec3(-0.4f, -0.3f, -0.2f)), .color = glm::vec3(1, 1, 1), }); - bvh.addModels(ModelLoader::loadModel("../res/models/stanford-bunny.obj"), - glm::mat4(glm::vec4(1.0f, 0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 1.0f, 0.0f, 0.0f), glm::vec4(0.0f, 0.0f, 1.0f, 0.0f), - glm::vec4(0.0f, 0.0f, 0.0f, 1.0f))); - bvh.generate(); + scene->addModels(ModelLoader::loadModel("../res/models/cube.fbx"), + glm::mat4(glm::vec4(1.0f, 0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 1.0f, 0.0f, 0.0f), glm::vec4(0.0f, 0.0f, 1.0f, 0.0f), + glm::vec4(0.0f, 0.0f, 0.0f, 1.0f))); + scene->generate(); } Renderer::~Renderer() {} @@ -94,7 +97,7 @@ void Renderer::render(Camera camera, RenderParameter params) glm::vec3 focus = r.origin + t * r.direction; // r = Ray(lensSample, normalize(focus - lensSample)); // TODO: Fix lens - bvh.traceRay(r, payload, 1e-4, 1e20); + scene->traceRay(r, payload, 1e-4, 1e20); accumulator[w + h * params.width] += payload.accumulatedRadiance / float(params.numSamples); } diff --git a/src/scene/Renderer.h b/src/scene/Renderer.h index 7833d29..8eef744 100644 --- a/src/scene/Renderer.h +++ b/src/scene/Renderer.h @@ -7,9 +7,9 @@ struct RenderParameter { - int width; - int height; - int numSamples; + uint32_t width; + uint32_t height; + uint32_t numSamples; }; class Renderer @@ -26,7 +26,7 @@ public: return std::accumulate(sampleTimes.begin(), sampleTimes.end(), 0.0f) / sampleTimes.size(); } -private: +protected: virtual void render(Camera cam, RenderParameter params); ThreadPool threadPool; std::thread worker; @@ -40,5 +40,5 @@ private: std::vector accumulator; std::vector pointLights; std::vector directionalLights; - Scene bvh; + std::unique_ptr scene; }; \ No newline at end of file diff --git a/src/scene/Scene.cpp b/src/scene/Scene.cpp index 60da458..50870f0 100644 --- a/src/scene/Scene.cpp +++ b/src/scene/Scene.cpp @@ -20,42 +20,32 @@ void Scene::addModels(std::vector _models, glm::mat4 transform) void Scene::generate() { - std::vector pendingNodes; - for (const auto& [model, ref] : std::views::zip(models, refs)) + // todo: clear everything + for (uint32_t i = 0; i < models.size(); ++i) { - pendingNodes.push_back(std::make_unique(model->boundingBox, ref)); - } - while (pendingNodes.size() > 1) - { - int lhs = pendingNodes.size(); - int rhs = pendingNodes.size(); - float minSurface = std::numeric_limits::max(); - for (int i = 0; i < pendingNodes.size(); ++i) + auto& model = models[i]; + ModelReference ref = { + .positionOffset = (uint32_t)positionPool.size(), + .numPositions = (uint32_t)model->positions.size(), + .indicesOffset = (uint32_t)indicesPool.size(), + .numIndices = (uint32_t)model->indices.size(), + }; + for (uint32_t i = 0; i < model->positions.size(); ++i) { - for (int j = 0; j < pendingNodes.size(); ++j) - { - if (i == j) - continue; - AABB combined = AABB::combine(pendingNodes[i]->aabb, pendingNodes[j]->aabb); - float surface = combined.surfaceArea(); - if (minSurface > surface) - { - lhs = i; - rhs = j; - minSurface = surface; - } - } + positionPool.push_back(model->positions[i]); + texCoordsPool.push_back(model->texCoords[i]); + normalsPool.push_back(model->normals[i]); } - PNode newNode = std::make_unique(AABB::combine(pendingNodes[lhs]->aabb, pendingNodes[rhs]->aabb)); - newNode->left = std::move(pendingNodes[lhs]); - newNode->right = std::move(pendingNodes[rhs]); - assert(rhs > lhs); - // - pendingNodes.erase(pendingNodes.begin() + rhs); - pendingNodes.erase(pendingNodes.begin() + lhs); - pendingNodes.push_back(std::move(newNode)); + for (uint32_t i = 0; i < model->indices.size(); ++i) + { + indicesPool.push_back(model->indices[i]); + edgesPool.push_back(model->edges[i * 2 + 0]); + edgesPool.push_back(model->edges[i * 2 + 1]); + faceNormalsPool.push_back(glm::normalize(model->faceNormals[i])); + } + refs.push_back(ref); } - hierarchy = std::move(pendingNodes[0]); + createRayTracingHierarchy(); } void Scene::traceRay(Ray ray, Payload& payload, const float tmin, const float tmax) const noexcept @@ -119,33 +109,44 @@ void Scene::traceRay(Ray ray, Payload& payload, const float tmin, const float tm } } -void Scene::populateGeometryPools() +void Scene::createRayTracingHierarchy() { - //todo: clear everything - for(uint32_t i = 0; i < models.size(); ++i) + std::vector pendingNodes; + for (const auto& [model, ref] : std::views::zip(models, refs)) { - auto& model = models[i]; - ModelReference ref = { - .positionOffset = (uint32_t)positionPool.size(), - .numPositions = (uint32_t)model->positions.size(), - .indicesOffset = (uint32_t)indicesPool.size(), - .numIndices = (uint32_t)model->indices.size(), - }; - for (uint32_t i = 0; i < model->positions.size(); ++i) - { - positionPool.push_back(model->positions[i]); - texCoordsPool.push_back(model->texCoords[i]); - normalsPool.push_back(model->normals[i]); - } - for (uint32_t i = 0; i < model->indices.size(); ++i) - { - indicesPool.push_back(model->indices[i]); - edgesPool.push_back(model->edges[i * 2 + 0]); - edgesPool.push_back(model->edges[i * 2 + 1]); - faceNormalsPool.push_back(glm::normalize(model->faceNormals[i])); - } - refs.push_back(ref); + pendingNodes.push_back(std::make_unique(model->boundingBox, ref)); } + while (pendingNodes.size() > 1) + { + int lhs = pendingNodes.size(); + int rhs = pendingNodes.size(); + float minSurface = std::numeric_limits::max(); + for (int i = 0; i < pendingNodes.size(); ++i) + { + for (int j = 0; j < pendingNodes.size(); ++j) + { + if (i == j) + continue; + AABB combined = AABB::combine(pendingNodes[i]->aabb, pendingNodes[j]->aabb); + float surface = combined.surfaceArea(); + if (minSurface > surface) + { + lhs = i; + rhs = j; + minSurface = surface; + } + } + } + PNode newNode = std::make_unique(AABB::combine(pendingNodes[lhs]->aabb, pendingNodes[rhs]->aabb)); + newNode->left = std::move(pendingNodes[lhs]); + newNode->right = std::move(pendingNodes[rhs]); + assert(rhs > lhs); + // + pendingNodes.erase(pendingNodes.begin() + rhs); + pendingNodes.erase(pendingNodes.begin() + lhs); + pendingNodes.push_back(std::move(newNode)); + } + hierarchy = std::move(pendingNodes[0]); } bool Scene::testIntersection(const PNode& currentNode, const Ray ray, const float tmin, float tmax) const noexcept diff --git a/src/scene/Scene.h b/src/scene/Scene.h index 585c437..da47e24 100644 --- a/src/scene/Scene.h +++ b/src/scene/Scene.h @@ -37,7 +37,7 @@ public: void addDirectionalLight(DirectionalLight dir) { directionalLights.push_back(dir); } void addModel(PModel model, glm::mat4 transform); void addModels(std::vector models, glm::mat4 transform); - virtual void generate(); + void generate(); void traceRay(Ray ray, Payload& payload, const float tmin, const float tmax) const noexcept; @@ -66,11 +66,12 @@ protected: PNode hierarchy; std::vector models; - void populateGeometryPools(); + virtual void createRayTracingHierarchy(); // tests if a ray intersects any geometry, no hit information, for shadow rays bool testIntersection(const PNode& currentNode, const Ray ray, const float tmin, const float tmax) const noexcept; IntersectionInfo generateIntersections(const PNode& currentNode, const Ray ray, const float tmin, const float tmax) const noexcept; bool testModel(const ModelReference& reference, const Ray ray, const float tmin, const float tmax) const noexcept; IntersectionInfo intersectModel(const ModelReference& reference, const Ray ray, const float tmin, const float tmax) const noexcept; + friend class GPURenderer; }; \ No newline at end of file diff --git a/src/util/ModelLoader.cpp b/src/util/ModelLoader.cpp index 02ffc1a..1ea6905 100644 --- a/src/util/ModelLoader.cpp +++ b/src/util/ModelLoader.cpp @@ -8,7 +8,7 @@ std::vector ModelLoader::loadModel(std::string_view filename) { Assimp::Importer importer; - const aiScene* scene = importer.ReadFile(std::string(filename), aiProcess_Triangulate); + const aiScene* scene = importer.ReadFile(std::string(filename), aiProcess_Triangulate | aiProcess_GenNormals); std::cout << importer.GetErrorString() << std::endl; std::vector result; for (int m = 0; m < scene->mNumMeshes; ++m) @@ -28,6 +28,7 @@ std::vector ModelLoader::loadModel(std::string_view filename) { model->texCoords.push_back(glm::vec2(0, 0)); } + model->normals.push_back(glm::vec3(mesh->mNormals[v].x, mesh->mNormals[v].y, mesh->mNormals[v].z)); aabb.adjust(model->positions.back()); } for (int i = 0; i < mesh->mNumFaces; ++i)