mirror of
https://github.com/ZDoom/ZDRay.git
synced 2025-01-24 08:41:06 +00:00
Begin work on a new GPU raytracer that uses ray queries instead of the raytracing pipeline
This commit is contained in:
parent
04d8aabc92
commit
8ec576c3bd
5 changed files with 797 additions and 0 deletions
|
@ -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
|
||||
|
|
110
src/lightmap/glsl_frag.h
Normal file
110
src/lightmap/glsl_frag.h
Normal file
|
@ -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";
|
29
src/lightmap/glsl_vert.h
Normal file
29
src/lightmap/glsl_vert.h
Normal file
|
@ -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";
|
537
src/lightmap/gpuraytracer2.cpp
Normal file
537
src/lightmap/gpuraytracer2.cpp
Normal file
|
@ -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 <map>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include "glsl_frag.h"
|
||||
#include "glsl_vert.h"
|
||||
|
||||
extern bool VKDebug;
|
||||
|
||||
GPURaytracer2::GPURaytracer2()
|
||||
{
|
||||
device = std::make_unique<VulkanDevice>(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<VulkanCommandPool>(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<VulkanFence>(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<uint64_t>::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<SurfaceInfo2> 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<LightInfo2> 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<VulkanAccelerationStructure>(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<VulkanAccelerationStructure>(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<void()> callback)
|
||||
{
|
||||
std::exception_ptr e;
|
||||
std::condition_variable condvar;
|
||||
std::mutex m;
|
||||
bool stop;
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m);
|
||||
stop = false;
|
||||
}
|
||||
|
||||
std::thread t([&]() {
|
||||
try
|
||||
{
|
||||
callback();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
e = std::current_exception();
|
||||
}
|
||||
std::unique_lock<std::mutex> lock(m);
|
||||
stop = true;
|
||||
lock.unlock();
|
||||
condvar.notify_all();
|
||||
});
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m);
|
||||
while (!stop)
|
||||
{
|
||||
condvar.wait_for(lock, std::chrono::milliseconds(500), [&]() { return stop; });
|
||||
}
|
||||
}
|
||||
|
||||
t.join();
|
||||
|
||||
if (e)
|
||||
std::rethrow_exception(e);
|
||||
}
|
117
src/lightmap/gpuraytracer2.h
Normal file
117
src/lightmap/gpuraytracer2.h
Normal file
|
@ -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<void()> callback);
|
||||
|
||||
LevelMesh* mesh = nullptr;
|
||||
|
||||
uint8_t* mappedUniforms = nullptr;
|
||||
int uniformsIndex = 0;
|
||||
int uniformStructs = 256;
|
||||
VkDeviceSize uniformStructStride = sizeof(Uniforms2);
|
||||
|
||||
std::unique_ptr<VulkanDevice> device;
|
||||
|
||||
std::unique_ptr<VulkanBuffer> vertexBuffer;
|
||||
std::unique_ptr<VulkanBuffer> indexBuffer;
|
||||
std::unique_ptr<VulkanBuffer> transferBuffer;
|
||||
std::unique_ptr<VulkanBuffer> surfaceIndexBuffer;
|
||||
std::unique_ptr<VulkanBuffer> surfaceBuffer;
|
||||
std::unique_ptr<VulkanBuffer> lightBuffer;
|
||||
|
||||
std::unique_ptr<VulkanBuffer> blScratchBuffer;
|
||||
std::unique_ptr<VulkanBuffer> blAccelStructBuffer;
|
||||
std::unique_ptr<VulkanAccelerationStructure> blAccelStruct;
|
||||
|
||||
std::unique_ptr<VulkanBuffer> tlTransferBuffer;
|
||||
std::unique_ptr<VulkanBuffer> tlScratchBuffer;
|
||||
std::unique_ptr<VulkanBuffer> tlInstanceBuffer;
|
||||
std::unique_ptr<VulkanBuffer> tlAccelStructBuffer;
|
||||
std::unique_ptr<VulkanAccelerationStructure> tlAccelStruct;
|
||||
|
||||
std::unique_ptr<VulkanShader> vertShader;
|
||||
std::unique_ptr<VulkanShader> fragShader;
|
||||
|
||||
std::unique_ptr<VulkanDescriptorSetLayout> descriptorSetLayout;
|
||||
|
||||
std::unique_ptr<VulkanPipelineLayout> pipelineLayout;
|
||||
std::unique_ptr<VulkanPipeline> pipeline;
|
||||
|
||||
std::unique_ptr<VulkanRenderPass> renderPass;
|
||||
|
||||
std::unique_ptr<VulkanBuffer> uniformBuffer;
|
||||
std::unique_ptr<VulkanBuffer> uniformTransferBuffer;
|
||||
|
||||
std::unique_ptr<VulkanDescriptorPool> descriptorPool;
|
||||
std::unique_ptr<VulkanDescriptorSet> descriptorSet;
|
||||
|
||||
std::unique_ptr<VulkanCommandPool> cmdpool;
|
||||
std::unique_ptr<VulkanCommandBuffer> cmdbuffer;
|
||||
};
|
Loading…
Reference in a new issue