diff --git a/CMakeLists.txt b/CMakeLists.txt index 598e6f3..2f169b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -176,6 +176,8 @@ set( SOURCES src/lightmap/stacktrace.h src/lightmap/gpuraytracer.cpp src/lightmap/gpuraytracer.h + src/lightmap/gpuraytracer2.cpp + src/lightmap/gpuraytracer2.h src/lightmap/glsl_rchit_bounce.h src/lightmap/glsl_rchit_light.h src/lightmap/glsl_rchit_sun.h @@ -187,6 +189,8 @@ set( SOURCES src/lightmap/glsl_rmiss_light.h src/lightmap/glsl_rmiss_sun.h src/lightmap/glsl_rmiss_ambient.h + src/lightmap/glsl_frag.h + src/lightmap/glsl_vert.h src/lightmap/cpuraytracer.cpp src/lightmap/cpuraytracer.h src/math/mat.cpp diff --git a/src/lightmap/glsl_frag.h b/src/lightmap/glsl_frag.h new file mode 100644 index 0000000..4cac922 --- /dev/null +++ b/src/lightmap/glsl_frag.h @@ -0,0 +1,110 @@ +static const char* glsl_frag = R"glsl( + +#version 460 +#extension GL_EXT_ray_query : require + +layout(set = 0, binding = 0) uniform accelerationStructureEXT acc; + +layout(set = 0, binding = 1) uniform Uniforms +{ + vec3 SunDir; + float Padding1; + vec3 SunColor; + float SunIntensity; +}; + +struct SurfaceInfo +{ + vec3 Normal; + float EmissiveDistance; + vec3 EmissiveColor; + float EmissiveIntensity; + float Sky; + float SamplingDistance; + float Padding1, Padding2; +}; + +struct LightInfo +{ + vec3 Origin; + float Padding0; + float Radius; + float Intensity; + float InnerAngleCos; + float OuterAngleCos; + vec3 SpotDir; + float Padding1; + vec3 Color; + float Padding2; +}; + +layout(set = 0, binding = 2) buffer SurfaceIndexBuffer { uint surfaceIndices[]; }; +layout(set = 0, binding = 3) buffer SurfaceBuffer { SurfaceInfo surfaces[]; }; +layout(set = 0, binding = 4) buffer LightBuffer { LightInfo lights[]; }; + +layout(push_constant) uniform PushConstants +{ + uint LightStart; + uint LightEnd; + int surfaceIndex; + int pushPadding; +}; + +layout(location = 0) in vec3 worldpos; +layout(location = 0) out vec4 fragcolor; + +void main() +{ + const float minDistance = 0.01; + + vec3 origin = worldpos; + vec3 normal; + if (surfaceIndex >= 0) + { + normal = surfaces[surfaceIndex].Normal; + origin += normal * 0.1; + } + + for (uint j = LightStart; j < LightEnd; j++) + { + LightInfo light = lights[j]; + + float dist = distance(light.Origin, origin); + if (dist > minDistance && dist < light.Radius) + { + vec3 dir = normalize(light.Origin - origin); + + float distAttenuation = max(1.0 - (dist / light.Radius), 0.0); + float angleAttenuation = 1.0f; + if (surfaceIndex >= 0) + { + angleAttenuation = max(dot(normal, dir), 0.0); + } + float spotAttenuation = 1.0; + if (light.OuterAngleCos > -1.0) + { + float cosDir = dot(dir, light.SpotDir); + spotAttenuation = smoothstep(light.OuterAngleCos, light.InnerAngleCos, cosDir); + spotAttenuation = max(spotAttenuation, 0.0); + } + + float attenuation = distAttenuation * angleAttenuation * spotAttenuation; + if (attenuation > 0.0) + { + rayQueryEXT rayQuery; + rayQueryInitializeEXT(rayQuery, acc, gl_RayFlagsTerminateOnFirstHitEXT, 0xFF, origin, minDistance, dir, dist); + + while(rayQueryProceedEXT(rayQuery)) { } + + if (rayQueryGetIntersectionTypeEXT(rayQuery, true) == gl_RayQueryCommittedIntersectionNoneEXT) + { + incoming.rgb += light.Color * (attenuation * light.Intensity) * incoming.w; + } + } + } + } + + fragcolor = vec4(0.0); +} + +)glsl"; diff --git a/src/lightmap/glsl_vert.h b/src/lightmap/glsl_vert.h new file mode 100644 index 0000000..1d61ded --- /dev/null +++ b/src/lightmap/glsl_vert.h @@ -0,0 +1,29 @@ +static const char* glsl_vert = R"glsl( + +#version 460 + +layout(set = 0, binding = 1) uniform Uniforms +{ + vec3 SunDir; + float Padding1; + vec3 SunColor; + float SunIntensity; +}; + +layout(push_constant) uniform PushConstants +{ + uint LightStart; + uint LightEnd; + int surfaceIndex; + int pushPadding; +}; + +layout(location = 0) out vec3 worldpos; + +void main() +{ + worldpos = vec3(0.0); + gl_Position = vec4(0.0, 0.0, 0.0, 1.0); +} + +)glsl"; diff --git a/src/lightmap/gpuraytracer2.cpp b/src/lightmap/gpuraytracer2.cpp new file mode 100644 index 0000000..6ef5fa2 --- /dev/null +++ b/src/lightmap/gpuraytracer2.cpp @@ -0,0 +1,537 @@ + +#include "math/mathlib.h" +#include "levelmesh.h" +#include "level/level.h" +#include "gpuraytracer2.h" +#include "framework/binfile.h" +#include "framework/templates.h" +#include "framework/halffloat.h" +#include "vulkanbuilders.h" +#include +#include +#include +#include +#include +#include +#include +#include "glsl_frag.h" +#include "glsl_vert.h" + +extern bool VKDebug; + +GPURaytracer2::GPURaytracer2() +{ + device = std::make_unique(0, VKDebug); + PrintVulkanInfo(); +} + +GPURaytracer2::~GPURaytracer2() +{ +} + +void GPURaytracer2::Raytrace(LevelMesh* level) +{ + mesh = level; + + printf("Building Vulkan acceleration structures\n"); + + if (device->renderdoc) + device->renderdoc->StartFrameCapture(0, 0); + + CreateVulkanObjects(); + + printf("Ray tracing in progress...\n"); + + RunAsync([&]() { + }); + + if (device->renderdoc) + device->renderdoc->EndFrameCapture(0, 0); + + printf("Ray trace complete\n"); +} + +void GPURaytracer2::CreateVulkanObjects() +{ + cmdpool = std::make_unique(device.get(), device->graphicsFamily); + cmdbuffer = cmdpool->createBuffer(); + cmdbuffer->begin(); + + CreateVertexAndIndexBuffers(); + CreateBottomLevelAccelerationStructure(); + CreateTopLevelAccelerationStructure(); + CreateShaders(); + CreatePipeline(); + CreateDescriptorSet(); + + PipelineBarrier() + .AddMemory(VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR, VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR) + .Execute(cmdbuffer.get(), VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); +} + +void GPURaytracer2::SubmitCommands() +{ + auto submitFence = std::make_unique(device.get()); + + QueueSubmit() + .AddCommandBuffer(cmdbuffer.get()) + .Execute(device.get(), device->graphicsQueue, submitFence.get()); + + VkResult result = vkWaitForFences(device->device, 1, &submitFence->fence, VK_TRUE, std::numeric_limits::max()); + if (result != VK_SUCCESS) + throw std::runtime_error("vkWaitForFences failed"); + result = vkResetFences(device->device, 1, &submitFence->fence); + if (result != VK_SUCCESS) + throw std::runtime_error("vkResetFences failed"); + cmdbuffer.reset(); +} + +void GPURaytracer2::CreateVertexAndIndexBuffers() +{ + std::vector surfaces; + surfaces.reserve(mesh->surfaces.size()); + for (const auto& surface : mesh->surfaces) + { + SurfaceLightDef* def = nullptr; + if (surface->type >= ST_MIDDLESIDE && surface->type <= ST_LOWERSIDE) + { + int lightdefidx = mesh->map->Sides[surface->typeIndex].lightdef; + if (lightdefidx != -1) + { + def = &mesh->map->SurfaceLights[lightdefidx]; + } + } + else if (surface->type == ST_FLOOR || surface->type == ST_CEILING) + { + MapSubsectorEx* sub = &mesh->map->GLSubsectors[surface->typeIndex]; + IntSector* sector = mesh->map->GetSectorFromSubSector(sub); + + if (sector && surface->numVerts > 0) + { + if (sector->floorlightdef != -1 && surface->type == ST_FLOOR) + { + def = &mesh->map->SurfaceLights[sector->floorlightdef]; + } + else if (sector->ceilinglightdef != -1 && surface->type == ST_CEILING) + { + def = &mesh->map->SurfaceLights[sector->ceilinglightdef]; + } + } + } + + SurfaceInfo2 info; + info.Sky = surface->bSky ? 1.0f : 0.0f; + info.Normal = surface->plane.Normal(); + if (def) + { + info.EmissiveDistance = def->distance + def->distance; + info.EmissiveIntensity = def->intensity; + info.EmissiveColor = def->rgb; + } + else + { + info.EmissiveDistance = 0.0f; + info.EmissiveIntensity = 0.0f; + info.EmissiveColor = vec3(0.0f, 0.0f, 0.0f); + } + + info.SamplingDistance = float(surface->sampleDimension); + surfaces.push_back(info); + } + + std::vector lights; + for (ThingLight& light : mesh->map->ThingLights) + { + LightInfo2 info; + info.Origin = light.LightOrigin(); + info.Radius = light.LightRadius(); + info.Intensity = light.intensity; + info.InnerAngleCos = light.innerAngleCos; + info.OuterAngleCos = light.outerAngleCos; + info.SpotDir = light.SpotDir(); + info.Color = light.rgb; + lights.push_back(info); + } + + if (lights.empty()) // vulkan doesn't support zero byte buffers + lights.push_back(LightInfo2()); + + size_t vertexbuffersize = (size_t)mesh->MeshVertices.Size() * sizeof(vec3); + size_t indexbuffersize = (size_t)mesh->MeshElements.Size() * sizeof(uint32_t); + size_t surfaceindexbuffersize = (size_t)mesh->MeshSurfaces.Size() * sizeof(uint32_t); + size_t surfacebuffersize = (size_t)surfaces.size() * sizeof(SurfaceInfo2); + size_t lightbuffersize = (size_t)lights.size() * sizeof(LightInfo2); + size_t transferbuffersize = vertexbuffersize + indexbuffersize + surfaceindexbuffersize + surfacebuffersize + lightbuffersize; + size_t vertexoffset = 0; + size_t indexoffset = vertexoffset + vertexbuffersize; + size_t surfaceindexoffset = indexoffset + indexbuffersize; + size_t surfaceoffset = surfaceindexoffset + surfaceindexbuffersize; + size_t lightoffset = surfaceoffset + surfacebuffersize; + + vertexBuffer = BufferBuilder() + .Usage(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) + .Size(vertexbuffersize) + .DebugName("vertexBuffer") + .Create(device.get()); + + indexBuffer = BufferBuilder() + .Usage(VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) + .Size(indexbuffersize) + .DebugName("indexBuffer") + .Create(device.get()); + + surfaceIndexBuffer = BufferBuilder() + .Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT) + .Size(surfaceindexbuffersize) + .DebugName("surfaceIndexBuffer") + .Create(device.get()); + + surfaceBuffer = BufferBuilder() + .Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT) + .Size(surfacebuffersize) + .DebugName("surfaceBuffer") + .Create(device.get()); + + lightBuffer = BufferBuilder() + .Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT) + .Size(lightbuffersize) + .DebugName("lightBuffer") + .Create(device.get()); + + transferBuffer = BufferBuilder() + .Usage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY) + .Size(transferbuffersize) + .DebugName("transferBuffer") + .Create(device.get()); + + uint8_t* data = (uint8_t*)transferBuffer->Map(0, transferbuffersize); + memcpy(data + vertexoffset, mesh->MeshVertices.Data(), vertexbuffersize); + memcpy(data + indexoffset, mesh->MeshElements.Data(), indexbuffersize); + memcpy(data + surfaceindexoffset, mesh->MeshSurfaces.Data(), surfaceindexbuffersize); + memcpy(data + surfaceoffset, surfaces.data(), surfacebuffersize); + memcpy(data + lightoffset, lights.data(), lightbuffersize); + transferBuffer->Unmap(); + + cmdbuffer->copyBuffer(transferBuffer.get(), vertexBuffer.get(), vertexoffset); + cmdbuffer->copyBuffer(transferBuffer.get(), indexBuffer.get(), indexoffset); + cmdbuffer->copyBuffer(transferBuffer.get(), surfaceIndexBuffer.get(), surfaceindexoffset); + cmdbuffer->copyBuffer(transferBuffer.get(), surfaceBuffer.get(), surfaceoffset); + cmdbuffer->copyBuffer(transferBuffer.get(), lightBuffer.get(), lightoffset); + + VkMemoryBarrier barrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER }; + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR; + cmdbuffer->pipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, 0, 1, &barrier, 0, nullptr, 0, nullptr); +} + +void GPURaytracer2::CreateBottomLevelAccelerationStructure() +{ + VkBufferDeviceAddressInfo info = { VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO }; + info.buffer = vertexBuffer->buffer; + VkDeviceAddress vertexAddress = vkGetBufferDeviceAddress(device->device, &info); + + info = { VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO }; + info.buffer = indexBuffer->buffer; + VkDeviceAddress indexAddress = vkGetBufferDeviceAddress(device->device, &info); + + VkAccelerationStructureGeometryTrianglesDataKHR triangles = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR }; + triangles.vertexFormat = VK_FORMAT_R32G32B32_SFLOAT; + triangles.vertexData.deviceAddress = vertexAddress; + triangles.vertexStride = sizeof(vec3); + triangles.indexType = VK_INDEX_TYPE_UINT32; + triangles.indexData.deviceAddress = indexAddress; + triangles.maxVertex = mesh->MeshVertices.Size(); + + VkAccelerationStructureGeometryKHR accelStructBLDesc = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR }; + accelStructBLDesc.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR; + accelStructBLDesc.flags = VK_GEOMETRY_OPAQUE_BIT_KHR; + accelStructBLDesc.geometry.triangles = triangles; + + VkAccelerationStructureBuildRangeInfoKHR rangeInfo = {}; + rangeInfo.primitiveCount = mesh->MeshElements.Size() / 3; + + VkAccelerationStructureBuildGeometryInfoKHR buildInfo = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR }; + buildInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR; + buildInfo.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR; + buildInfo.flags = accelStructBLDesc.flags | VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR; + buildInfo.geometryCount = 1; + buildInfo.pGeometries = &accelStructBLDesc; + + uint32_t maxPrimitiveCount = rangeInfo.primitiveCount; + + VkAccelerationStructureBuildSizesInfoKHR sizeInfo = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR }; + vkGetAccelerationStructureBuildSizesKHR(device->device, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &buildInfo, &maxPrimitiveCount, &sizeInfo); + + blAccelStructBuffer = BufferBuilder() + .Usage(VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT) + .Size(sizeInfo.accelerationStructureSize) + .DebugName("blAccelStructBuffer") + .Create(device.get()); + + VkAccelerationStructureKHR blAccelStructHandle = {}; + VkAccelerationStructureCreateInfoKHR createInfo = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR }; + createInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR; + createInfo.buffer = blAccelStructBuffer->buffer; + createInfo.size = sizeInfo.accelerationStructureSize; + VkResult result = vkCreateAccelerationStructureKHR(device->device, &createInfo, nullptr, &blAccelStructHandle); + if (result != VK_SUCCESS) + throw std::runtime_error("vkCreateAccelerationStructureKHR failed"); + blAccelStruct = std::make_unique(device.get(), blAccelStructHandle); + + blScratchBuffer = BufferBuilder() + .Usage(VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) + .Size(sizeInfo.buildScratchSize) + .DebugName("blScratchBuffer") + .Create(device.get()); + + info = { VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO }; + info.buffer = blScratchBuffer->buffer; + VkDeviceAddress scratchAddress = vkGetBufferDeviceAddress(device->device, &info); + + buildInfo.dstAccelerationStructure = blAccelStruct->accelstruct; + buildInfo.scratchData.deviceAddress = scratchAddress; + VkAccelerationStructureBuildRangeInfoKHR* rangeInfos[] = { &rangeInfo }; + cmdbuffer->buildAccelerationStructures(1, &buildInfo, rangeInfos); +} + +void GPURaytracer2::CreateTopLevelAccelerationStructure() +{ + VkAccelerationStructureDeviceAddressInfoKHR addressInfo = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR }; + addressInfo.accelerationStructure = blAccelStruct->accelstruct; + VkDeviceAddress blAccelStructAddress = vkGetAccelerationStructureDeviceAddressKHR(device->device, &addressInfo); + + VkAccelerationStructureInstanceKHR instance = {}; + instance.transform.matrix[0][0] = 1.0f; + instance.transform.matrix[1][1] = 1.0f; + instance.transform.matrix[2][2] = 1.0f; + instance.instanceCustomIndex = 0; + instance.accelerationStructureReference = blAccelStructAddress; + instance.flags = VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR; + instance.mask = 0xff; + instance.instanceShaderBindingTableRecordOffset = 0; + + tlTransferBuffer = BufferBuilder() + .Usage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY) + .Size(sizeof(VkAccelerationStructureInstanceKHR)) + .DebugName("tlTransferBuffer") + .Create(device.get()); + + auto data = (uint8_t*)tlTransferBuffer->Map(0, sizeof(VkAccelerationStructureInstanceKHR)); + memcpy(data, &instance, sizeof(VkAccelerationStructureInstanceKHR)); + tlTransferBuffer->Unmap(); + + tlInstanceBuffer = BufferBuilder() + .Usage(VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | VK_BUFFER_USAGE_TRANSFER_DST_BIT) + .Size(sizeof(VkAccelerationStructureInstanceKHR)) + .DebugName("tlInstanceBuffer") + .Create(device.get()); + + cmdbuffer->copyBuffer(tlTransferBuffer.get(), tlInstanceBuffer.get()); + + VkMemoryBarrier barrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER }; + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR; + cmdbuffer->pipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, 0, 1, &barrier, 0, nullptr, 0, nullptr); + + VkBufferDeviceAddressInfo info = { VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO }; + info.buffer = tlInstanceBuffer->buffer; + VkDeviceAddress instanceBufferAddress = vkGetBufferDeviceAddress(device->device, &info); + + VkAccelerationStructureGeometryInstancesDataKHR instances = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_INSTANCES_DATA_KHR }; + instances.data.deviceAddress = instanceBufferAddress; + + VkAccelerationStructureGeometryKHR accelStructTLDesc = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR }; + accelStructTLDesc.geometryType = VK_GEOMETRY_TYPE_INSTANCES_KHR; + accelStructTLDesc.geometry.instances = instances; + + VkAccelerationStructureBuildRangeInfoKHR rangeInfo = {}; + rangeInfo.primitiveCount = 1; + + VkAccelerationStructureBuildGeometryInfoKHR buildInfo = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR }; + buildInfo.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR; + buildInfo.geometryCount = 1; + buildInfo.pGeometries = &accelStructTLDesc; + buildInfo.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR; + buildInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR; + buildInfo.srcAccelerationStructure = VK_NULL_HANDLE; + + uint32_t maxInstanceCount = 1; + + VkAccelerationStructureBuildSizesInfoKHR sizeInfo = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR }; + vkGetAccelerationStructureBuildSizesKHR(device->device, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &buildInfo, &maxInstanceCount, &sizeInfo); + + tlAccelStructBuffer = BufferBuilder() + .Usage(VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT) + .Size(sizeInfo.accelerationStructureSize) + .DebugName("tlAccelStructBuffer") + .Create(device.get()); + + VkAccelerationStructureKHR tlAccelStructHandle = {}; + VkAccelerationStructureCreateInfoKHR createInfo = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR }; + createInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR; + createInfo.buffer = tlAccelStructBuffer->buffer; + createInfo.size = sizeInfo.accelerationStructureSize; + VkResult result = vkCreateAccelerationStructureKHR(device->device, &createInfo, nullptr, &tlAccelStructHandle); + if (result != VK_SUCCESS) + throw std::runtime_error("vkCreateAccelerationStructureKHR failed"); + tlAccelStruct = std::make_unique(device.get(), tlAccelStructHandle); + + tlScratchBuffer = BufferBuilder() + .Usage(VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) + .Size(sizeInfo.buildScratchSize) + .DebugName("tlScratchBuffer") + .Create(device.get()); + + info = { VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO }; + info.buffer = tlScratchBuffer->buffer; + VkDeviceAddress scratchAddress = vkGetBufferDeviceAddress(device->device, &info); + + buildInfo.dstAccelerationStructure = tlAccelStruct->accelstruct; + buildInfo.scratchData.deviceAddress = scratchAddress; + + VkAccelerationStructureBuildRangeInfoKHR* rangeInfos[] = { &rangeInfo }; + cmdbuffer->buildAccelerationStructures(1, &buildInfo, rangeInfos); +} + +void GPURaytracer2::CreateShaders() +{ + vertShader = ShaderBuilder() + .VertexShader(glsl_vert) + .DebugName("vertShader") + .Create("vertShader", device.get()); + + fragShader = ShaderBuilder() + .FragmentShader(glsl_frag) + .DebugName("fragShader") + .Create("fragShader", device.get()); +} + +void GPURaytracer2::CreatePipeline() +{ + descriptorSetLayout = DescriptorSetLayoutBuilder() + .AddBinding(0, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1, VK_SHADER_STAGE_FRAGMENT_BIT) + .AddBinding(1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT) + .AddBinding(2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT) + .AddBinding(3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT) + .DebugName("descriptorSetLayout") + .Create(device.get()); + + pipelineLayout = PipelineLayoutBuilder() + .AddSetLayout(descriptorSetLayout.get()) + .AddPushConstantRange(VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushConstants2)) + .DebugName("pipelineLayout") + .Create(device.get()); + + renderPass = RenderPassBuilder() + .DebugName("renderpass") + .Create(device.get()); + + pipeline = GraphicsPipelineBuilder() + .Layout(pipelineLayout.get()) + .RenderPass(renderPass.get()) + .AddVertexShader(vertShader.get()) + .AddFragmentShader(fragShader.get()) + .DebugName("pipeline") + .Create(device.get()); +} + +void GPURaytracer2::CreateDescriptorSet() +{ + VkDeviceSize align = device->physicalDevice.properties.limits.minUniformBufferOffsetAlignment; + uniformStructStride = (sizeof(Uniforms2) + align - 1) / align * align; + + uniformBuffer = BufferBuilder() + .Usage(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT) + .Size(uniformStructs * uniformStructStride) + .DebugName("uniformBuffer") + .Create(device.get()); + + uniformTransferBuffer = BufferBuilder() + .Usage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU) + .Size(uniformStructs * uniformStructStride) + .DebugName("uniformTransferBuffer") + .Create(device.get()); + + descriptorPool = DescriptorPoolBuilder() + .AddPoolSize(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1) + .AddPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1) + .AddPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 3) + .MaxSets(1) + .DebugName("descriptorPool") + .Create(device.get()); + + descriptorSet = descriptorPool->allocate(descriptorSetLayout.get()); + descriptorSet->SetDebugName("descriptorSet"); + + WriteDescriptors() + .AddAccelerationStructure(descriptorSet.get(), 0, tlAccelStruct.get()) + .AddBuffer(descriptorSet.get(), 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, uniformBuffer.get(), 0, sizeof(Uniforms2)) + .AddBuffer(descriptorSet.get(), 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, surfaceIndexBuffer.get()) + .AddBuffer(descriptorSet.get(), 3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, surfaceBuffer.get()) + .AddBuffer(descriptorSet.get(), 4, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, lightBuffer.get()) + .Execute(device.get()); +} + +void GPURaytracer2::PrintVulkanInfo() +{ + const auto& props = device->physicalDevice.properties; + + std::string deviceType; + switch (props.deviceType) + { + case VK_PHYSICAL_DEVICE_TYPE_OTHER: deviceType = "other"; break; + case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: deviceType = "integrated gpu"; break; + case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: deviceType = "discrete gpu"; break; + case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: deviceType = "virtual gpu"; break; + case VK_PHYSICAL_DEVICE_TYPE_CPU: deviceType = "cpu"; break; + default: deviceType = std::to_string(props.deviceType); break; + } + + std::string apiVersion = std::to_string(VK_VERSION_MAJOR(props.apiVersion)) + "." + std::to_string(VK_VERSION_MINOR(props.apiVersion)) + "." + std::to_string(VK_VERSION_PATCH(props.apiVersion)); + std::string driverVersion = std::to_string(VK_VERSION_MAJOR(props.driverVersion)) + "." + std::to_string(VK_VERSION_MINOR(props.driverVersion)) + "." + std::to_string(VK_VERSION_PATCH(props.driverVersion)); + + printf("Vulkan device: %s\n", props.deviceName); + printf("Vulkan device type: %s\n", deviceType.c_str()); + printf("Vulkan version: %s (api) %s (driver)\n", apiVersion.c_str(), driverVersion.c_str()); +} + +void GPURaytracer2::RunAsync(std::function callback) +{ + std::exception_ptr e; + std::condition_variable condvar; + std::mutex m; + bool stop; + + { + std::unique_lock lock(m); + stop = false; + } + + std::thread t([&]() { + try + { + callback(); + } + catch (...) + { + e = std::current_exception(); + } + std::unique_lock lock(m); + stop = true; + lock.unlock(); + condvar.notify_all(); + }); + + { + std::unique_lock lock(m); + while (!stop) + { + condvar.wait_for(lock, std::chrono::milliseconds(500), [&]() { return stop; }); + } + } + + t.join(); + + if (e) + std::rethrow_exception(e); +} diff --git a/src/lightmap/gpuraytracer2.h b/src/lightmap/gpuraytracer2.h new file mode 100644 index 0000000..a2d9765 --- /dev/null +++ b/src/lightmap/gpuraytracer2.h @@ -0,0 +1,117 @@ + +#pragma once + +#include "vulkandevice.h" +#include "vulkanobjects.h" + +class LevelMesh; + +struct Uniforms2 +{ + vec3 SunDir; + float Padding1; + vec3 SunColor; + float SunIntensity; +}; + +struct PushConstants2 +{ + uint32_t LightStart; + uint32_t LightEnd; + int32_t surfaceIndex; + int32_t pushPadding; +}; + +struct SurfaceInfo2 +{ + vec3 Normal; + float EmissiveDistance; + vec3 EmissiveColor; + float EmissiveIntensity; + float Sky; + float SamplingDistance; + float Padding1, Padding2; +}; + +struct LightInfo2 +{ + vec3 Origin; + float Padding0; + float Radius; + float Intensity; + float InnerAngleCos; + float OuterAngleCos; + vec3 SpotDir; + float Padding1; + vec3 Color; + float Padding2; +}; + +class GPURaytracer2 +{ +public: + GPURaytracer2(); + ~GPURaytracer2(); + + void Raytrace(LevelMesh* level); + +private: + void CreateVulkanObjects(); + void CreateVertexAndIndexBuffers(); + void CreateBottomLevelAccelerationStructure(); + void CreateTopLevelAccelerationStructure(); + void CreateShaders(); + void CreatePipeline(); + void CreateDescriptorSet(); + + void SubmitCommands(); + + void PrintVulkanInfo(); + + static void RunAsync(std::function callback); + + LevelMesh* mesh = nullptr; + + uint8_t* mappedUniforms = nullptr; + int uniformsIndex = 0; + int uniformStructs = 256; + VkDeviceSize uniformStructStride = sizeof(Uniforms2); + + std::unique_ptr device; + + std::unique_ptr vertexBuffer; + std::unique_ptr indexBuffer; + std::unique_ptr transferBuffer; + std::unique_ptr surfaceIndexBuffer; + std::unique_ptr surfaceBuffer; + std::unique_ptr lightBuffer; + + std::unique_ptr blScratchBuffer; + std::unique_ptr blAccelStructBuffer; + std::unique_ptr blAccelStruct; + + std::unique_ptr tlTransferBuffer; + std::unique_ptr tlScratchBuffer; + std::unique_ptr tlInstanceBuffer; + std::unique_ptr tlAccelStructBuffer; + std::unique_ptr tlAccelStruct; + + std::unique_ptr vertShader; + std::unique_ptr fragShader; + + std::unique_ptr descriptorSetLayout; + + std::unique_ptr pipelineLayout; + std::unique_ptr pipeline; + + std::unique_ptr renderPass; + + std::unique_ptr uniformBuffer; + std::unique_ptr uniformTransferBuffer; + + std::unique_ptr descriptorPool; + std::unique_ptr descriptorSet; + + std::unique_ptr cmdpool; + std::unique_ptr cmdbuffer; +};