Add support for gl_light_raytrace for cards that do not support rayquery

This commit is contained in:
Magnus Norddahl 2023-04-12 02:09:28 +02:00 committed by Christoph Oelckers
parent 16ad25b382
commit 447932025f
5 changed files with 369 additions and 36 deletions

View file

@ -104,7 +104,15 @@ void VkDescriptorSetManager::UpdateFixedSet()
update.AddCombinedImageSampler(FixedSet.get(), 0, fb->GetTextureManager()->Shadowmap.View.get(), fb->GetSamplerManager()->ShadowmapSampler.get(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
update.AddCombinedImageSampler(FixedSet.get(), 1, fb->GetTextureManager()->Lightmap.View.get(), fb->GetSamplerManager()->LightmapSampler.get(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
if (fb->device->SupportsExtension(VK_KHR_RAY_QUERY_EXTENSION_NAME))
{
update.AddAccelerationStructure(FixedSet.get(), 2, fb->GetRaytrace()->GetAccelStruct());
}
else
{
update.AddBuffer(FixedSet.get(), 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, fb->GetRaytrace()->GetNodeBuffer());
update.AddBuffer(FixedSet.get(), 3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, fb->GetRaytrace()->GetVertexBuffer());
update.AddBuffer(FixedSet.get(), 4, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, fb->GetRaytrace()->GetIndexBuffer());
}
update.Execute(fb->device.get());
}
@ -264,7 +272,15 @@ void VkDescriptorSetManager::CreateFixedSetLayout()
builder.AddBinding(0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
builder.AddBinding(1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
if (fb->device->SupportsExtension(VK_KHR_RAY_QUERY_EXTENSION_NAME))
{
builder.AddBinding(2, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
}
else
{
builder.AddBinding(2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
builder.AddBinding(3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
builder.AddBinding(4, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
}
builder.DebugName("VkDescriptorSetManager.FixedSetLayout");
FixedSetLayout = builder.Create(fb->device.get());
}
@ -284,7 +300,13 @@ void VkDescriptorSetManager::CreateFixedSetPool()
DescriptorPoolBuilder poolbuilder;
poolbuilder.AddPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2 * maxSets);
if (fb->device->SupportsExtension(VK_KHR_RAY_QUERY_EXTENSION_NAME))
{
poolbuilder.AddPoolSize(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1 * maxSets);
}
else
{
poolbuilder.AddPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 3 * maxSets);
}
poolbuilder.MaxSets(maxSets);
poolbuilder.DebugName("VkDescriptorSetManager.FixedDescriptorPool");
FixedDescriptorPool = poolbuilder.Create(fb->device.get());

View file

@ -28,6 +28,8 @@
VkRaytrace::VkRaytrace(VulkanRenderDevice* fb) : fb(fb)
{
useRayQuery = fb->device->SupportsExtension(VK_KHR_RAY_QUERY_EXTENSION_NAME);
NullMesh.MeshVertices.Push({ -1.0f, -1.0f, -1.0f });
NullMesh.MeshVertices.Push({ 1.0f, -1.0f, -1.0f });
NullMesh.MeshVertices.Push({ 1.0f, 1.0f, -1.0f });
@ -43,6 +45,8 @@ VkRaytrace::VkRaytrace(VulkanRenderDevice* fb) : fb(fb)
for (int i = 0; i < 3 * 4; i++)
NullMesh.MeshElements.Push(i);
NullMesh.Collision = std::make_unique<TriangleMeshShape>(NullMesh.MeshVertices.Data(), NullMesh.MeshVertices.Size(), NullMesh.MeshElements.Data(), NullMesh.MeshElements.Size());
SetLevelMesh(nullptr);
}
@ -55,10 +59,7 @@ void VkRaytrace::SetLevelMesh(hwrenderer::LevelMesh* mesh)
{
Reset();
Mesh = mesh;
if (fb->device->SupportsExtension(VK_KHR_RAY_QUERY_EXTENSION_NAME))
{
CreateVulkanObjects();
}
CreateVulkanObjects();
}
}
@ -68,6 +69,7 @@ void VkRaytrace::Reset()
deletelist->Add(std::move(vertexBuffer));
deletelist->Add(std::move(indexBuffer));
deletelist->Add(std::move(transferBuffer));
deletelist->Add(std::move(nodesBuffer));
deletelist->Add(std::move(blScratchBuffer));
deletelist->Add(std::move(blAccelStructBuffer));
deletelist->Add(std::move(blAccelStruct));
@ -81,38 +83,35 @@ void VkRaytrace::Reset()
void VkRaytrace::CreateVulkanObjects()
{
CreateVertexAndIndexBuffers();
CreateBottomLevelAccelerationStructure();
CreateTopLevelAccelerationStructure();
if (useRayQuery)
{
CreateBottomLevelAccelerationStructure();
CreateTopLevelAccelerationStructure();
}
}
void VkRaytrace::CreateVertexAndIndexBuffers()
{
static_assert(sizeof(FVector3) == 3 * 4, "sizeof(FVector3) is not 12 bytes!");
std::vector<CollisionNode> nodes = CreateCollisionNodes();
size_t vertexbuffersize = (size_t)Mesh->MeshVertices.Size() * sizeof(FVector3);
size_t indexbuffersize = (size_t)Mesh->MeshElements.Size() * sizeof(uint32_t);
size_t transferbuffersize = vertexbuffersize + indexbuffersize;
size_t vertexoffset = 0;
size_t indexoffset = vertexoffset + vertexbuffersize;
// std430 alignment rules forces us to convert the vec3 to a vec4
std::vector<FVector4> vertices;
vertices.reserve(Mesh->MeshVertices.Size());
for (const FVector3& v : Mesh->MeshVertices)
vertices.push_back({ v, 1.0f });
transferBuffer = BufferBuilder()
.Usage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY)
.Size(transferbuffersize)
.DebugName("transferBuffer")
.Create(fb->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);
transferBuffer->Unmap();
CollisionNodeBufferHeader nodesHeader;
nodesHeader.root = Mesh->Collision->get_root();
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)
.Size(vertexbuffersize)
(useRayQuery ?
VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT |
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR : 0) |
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT)
.Size(vertices.size() * sizeof(FVector4))
.DebugName("vertexBuffer")
.Create(fb->device.get());
@ -120,19 +119,29 @@ void VkRaytrace::CreateVertexAndIndexBuffers()
.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)
.Size(indexbuffersize)
(useRayQuery ?
VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT |
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR : 0) |
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT)
.Size((size_t)Mesh->MeshElements.Size() * sizeof(uint32_t))
.DebugName("indexBuffer")
.Create(fb->device.get());
fb->GetCommands()->GetTransferCommands()->copyBuffer(transferBuffer.get(), vertexBuffer.get(), vertexoffset);
fb->GetCommands()->GetTransferCommands()->copyBuffer(transferBuffer.get(), indexBuffer.get(), indexoffset);
nodesBuffer = BufferBuilder()
.Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT)
.Size(sizeof(CollisionNodeBufferHeader) + nodes.size() * sizeof(CollisionNode))
.DebugName("nodesBuffer")
.Create(fb->device.get());
transferBuffer = BufferTransfer()
.AddBuffer(vertexBuffer.get(), vertices.data(), vertices.size() * sizeof(FVector4))
.AddBuffer(indexBuffer.get(), Mesh->MeshElements.Data(), (size_t)Mesh->MeshElements.Size() * sizeof(uint32_t))
.AddBuffer(nodesBuffer.get(), &nodesHeader, sizeof(CollisionNodeBufferHeader), nodes.data(), nodes.size() * sizeof(CollisionNode))
.Execute(fb->device.get(), fb->GetCommands()->GetTransferCommands());
// Finish transfer before using it for building
PipelineBarrier()
.AddMemory(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR);
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, useRayQuery ? VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR : VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
}
void VkRaytrace::CreateBottomLevelAccelerationStructure()
@ -146,9 +155,9 @@ void VkRaytrace::CreateBottomLevelAccelerationStructure()
accelStructBLDesc.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR;
accelStructBLDesc.flags = VK_GEOMETRY_OPAQUE_BIT_KHR;
accelStructBLDesc.geometry.triangles = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR };
accelStructBLDesc.geometry.triangles.vertexFormat = VK_FORMAT_R32G32B32_SFLOAT;
accelStructBLDesc.geometry.triangles.vertexFormat = VK_FORMAT_R32G32B32A32_SFLOAT;
accelStructBLDesc.geometry.triangles.vertexData.deviceAddress = vertexBuffer->GetDeviceAddress();
accelStructBLDesc.geometry.triangles.vertexStride = sizeof(FVector3);
accelStructBLDesc.geometry.triangles.vertexStride = sizeof(FVector4);
accelStructBLDesc.geometry.triangles.indexType = VK_INDEX_TYPE_UINT32;
accelStructBLDesc.geometry.triangles.indexData.deviceAddress = indexBuffer->GetDeviceAddress();
accelStructBLDesc.geometry.triangles.maxVertex = Mesh->MeshVertices.Size() - 1;
@ -277,3 +286,78 @@ void VkRaytrace::CreateTopLevelAccelerationStructure()
.AddMemory(VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR, VK_ACCESS_SHADER_READ_BIT)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
}
std::vector<CollisionNode> VkRaytrace::CreateCollisionNodes()
{
std::vector<CollisionNode> nodes;
nodes.reserve(Mesh->Collision->get_nodes().size());
for (const auto& node : Mesh->Collision->get_nodes())
{
CollisionNode info;
info.center = node.aabb.Center;
info.extents = node.aabb.Extents;
info.left = node.left;
info.right = node.right;
info.element_index = node.element_index;
nodes.push_back(info);
}
if (nodes.empty()) // vulkan doesn't support zero byte buffers
nodes.push_back(CollisionNode());
return nodes;
}
/////////////////////////////////////////////////////////////////////////////
BufferTransfer& BufferTransfer::AddBuffer(VulkanBuffer* buffer, const void* data, size_t size)
{
bufferCopies.push_back({ buffer, data, size, nullptr, 0 });
return *this;
}
BufferTransfer& BufferTransfer::AddBuffer(VulkanBuffer* buffer, const void* data0, size_t size0, const void* data1, size_t size1)
{
bufferCopies.push_back({ buffer, data0, size0, data1, size1 });
return *this;
}
std::unique_ptr<VulkanBuffer> BufferTransfer::Execute(VulkanDevice* device, VulkanCommandBuffer* cmdbuffer)
{
size_t transferbuffersize = 0;
for (const auto& copy : bufferCopies)
transferbuffersize += copy.size0 + copy.size1;
if (transferbuffersize == 0)
return nullptr;
auto transferBuffer = BufferBuilder()
.Usage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY)
.Size(transferbuffersize)
.DebugName("BufferTransfer.transferBuffer")
.Create(device);
uint8_t* data = (uint8_t*)transferBuffer->Map(0, transferbuffersize);
size_t pos = 0;
for (const auto& copy : bufferCopies)
{
memcpy(data + pos, copy.data0, copy.size0);
pos += copy.size0;
memcpy(data + pos, copy.data1, copy.size1);
pos += copy.size1;
}
transferBuffer->Unmap();
pos = 0;
for (const auto& copy : bufferCopies)
{
if (copy.size0 > 0)
cmdbuffer->copyBuffer(transferBuffer.get(), copy.buffer, pos, 0, copy.size0);
pos += copy.size0;
if (copy.size1 > 0)
cmdbuffer->copyBuffer(transferBuffer.get(), copy.buffer, pos, copy.size0, copy.size1);
pos += copy.size1;
}
return transferBuffer;
}

View file

@ -6,6 +6,26 @@
class VulkanRenderDevice;
struct CollisionNodeBufferHeader
{
int root;
int padding1;
int padding2;
int padding3;
};
struct CollisionNode
{
FVector3 center;
float padding1;
FVector3 extents;
float padding2;
int left;
int right;
int element_index;
int padding3;
};
class VkRaytrace
{
public:
@ -14,6 +34,9 @@ public:
void SetLevelMesh(hwrenderer::LevelMesh* mesh);
VulkanAccelerationStructure* GetAccelStruct() { return tlAccelStruct.get(); }
VulkanBuffer* GetVertexBuffer() { return vertexBuffer.get(); }
VulkanBuffer* GetIndexBuffer() { return indexBuffer.get(); }
VulkanBuffer* GetNodeBuffer() { return nodesBuffer.get(); }
private:
void Reset();
@ -22,14 +45,19 @@ private:
void CreateBottomLevelAccelerationStructure();
void CreateTopLevelAccelerationStructure();
std::vector<CollisionNode> CreateCollisionNodes();
VulkanRenderDevice* fb = nullptr;
bool useRayQuery = true;
hwrenderer::LevelMesh NullMesh;
hwrenderer::LevelMesh* Mesh = nullptr;
std::unique_ptr<VulkanBuffer> vertexBuffer;
std::unique_ptr<VulkanBuffer> indexBuffer;
std::unique_ptr<VulkanBuffer> transferBuffer;
std::unique_ptr<VulkanBuffer> nodesBuffer;
std::unique_ptr<VulkanBuffer> blScratchBuffer;
std::unique_ptr<VulkanBuffer> blAccelStructBuffer;
@ -41,3 +69,22 @@ private:
std::unique_ptr<VulkanBuffer> tlAccelStructBuffer;
std::unique_ptr<VulkanAccelerationStructure> tlAccelStruct;
};
class BufferTransfer
{
public:
BufferTransfer& AddBuffer(VulkanBuffer* buffer, const void* data, size_t size);
BufferTransfer& AddBuffer(VulkanBuffer* buffer, const void* data0, size_t size0, const void* data1, size_t size1);
std::unique_ptr<VulkanBuffer> Execute(VulkanDevice* device, VulkanCommandBuffer* cmdbuffer);
private:
struct BufferCopy
{
VulkanBuffer* buffer;
const void* data0;
size_t size0;
const void* data1;
size_t size1;
};
std::vector<BufferCopy> bufferCopies;
};

View file

@ -1,8 +1,32 @@
layout(set = 0, binding = 0) uniform sampler2D ShadowMap;
layout(set = 0, binding = 1) uniform sampler2DArray LightMap;
#if defined(USE_RAYTRACE) && defined(SUPPORTS_RAYQUERY)
#if defined(USE_RAYTRACE)
#if defined(SUPPORTS_RAYQUERY)
layout(set = 0, binding = 2) uniform accelerationStructureEXT TopLevelAS;
#else
struct CollisionNode
{
vec3 center;
float padding1;
vec3 extents;
float padding2;
int left;
int right;
int element_index;
int padding3;
};
layout(std430, set = 0, binding = 2) buffer NodeBuffer
{
int nodesRoot;
int nodebufferPadding1;
int nodebufferPadding2;
int nodebufferPadding3;
CollisionNode nodes[];
};
layout(std430, set = 0, binding = 3) buffer VertexBuffer { vec4 vertices[]; };
layout(std430, set = 0, binding = 4) buffer ElementBuffer { int elements[]; };
#endif
#endif
// This must match the HWViewpointUniforms struct

View file

@ -1,7 +1,9 @@
// Check if light is in shadow
#if defined(USE_RAYTRACE) && defined(SUPPORTS_RAYQUERY)
#if defined(USE_RAYTRACE)
#if defined(SUPPORTS_RAYQUERY)
bool traceHit(vec3 origin, vec3 direction, float dist)
{
@ -11,6 +13,160 @@ bool traceHit(vec3 origin, vec3 direction, float dist)
return rayQueryGetIntersectionTypeEXT(rayQuery, true) != gl_RayQueryCommittedIntersectionNoneEXT;
}
#else
struct RayBBox
{
vec3 start, end;
vec3 c, w, v;
};
RayBBox create_ray(vec3 ray_start, vec3 ray_end)
{
RayBBox ray;
ray.start = ray_start;
ray.end = ray_end;
ray.c = (ray_start + ray_end) * 0.5;
ray.w = ray_end - ray.c;
ray.v = abs(ray.w);
return ray;
}
bool overlap_bv_ray(RayBBox ray, int a)
{
vec3 v = ray.v;
vec3 w = ray.w;
vec3 h = nodes[a].extents;
vec3 c = ray.c - nodes[a].center;
if (abs(c.x) > v.x + h.x ||
abs(c.y) > v.y + h.y ||
abs(c.z) > v.z + h.z)
{
return false;
}
if (abs(c.y * w.z - c.z * w.y) > h.y * v.z + h.z * v.y ||
abs(c.x * w.z - c.z * w.x) > h.x * v.z + h.z * v.x ||
abs(c.x * w.y - c.y * w.x) > h.x * v.y + h.y * v.x)
{
return false;
}
return true;
}
#define FLT_EPSILON 1.192092896e-07F // smallest such that 1.0+FLT_EPSILON != 1.0
float intersect_triangle_ray(RayBBox ray, int a, out float barycentricB, out float barycentricC)
{
int start_element = nodes[a].element_index;
vec3 p[3];
p[0] = vertices[elements[start_element]].xyz;
p[1] = vertices[elements[start_element + 1]].xyz;
p[2] = vertices[elements[start_element + 2]].xyz;
// Moeller-Trumbore ray-triangle intersection algorithm:
vec3 D = ray.end - ray.start;
// Find vectors for two edges sharing p[0]
vec3 e1 = p[1] - p[0];
vec3 e2 = p[2] - p[0];
// Begin calculating determinant - also used to calculate u parameter
vec3 P = cross(D, e2);
float det = dot(e1, P);
// Backface check
//if (det < 0.0f)
// return 1.0f;
// If determinant is near zero, ray lies in plane of triangle
if (det > -FLT_EPSILON && det < FLT_EPSILON)
return 1.0f;
float inv_det = 1.0f / det;
// Calculate distance from p[0] to ray origin
vec3 T = ray.start - p[0];
// Calculate u parameter and test bound
float u = dot(T, P) * inv_det;
// Check if the intersection lies outside of the triangle
if (u < 0.f || u > 1.f)
return 1.0f;
// Prepare to test v parameter
vec3 Q = cross(T, e1);
// Calculate V parameter and test bound
float v = dot(D, Q) * inv_det;
// The intersection lies outside of the triangle
if (v < 0.f || u + v > 1.f)
return 1.0f;
float t = dot(e2, Q) * inv_det;
if (t <= FLT_EPSILON)
return 1.0f;
// Return hit location on triangle in barycentric coordinates
barycentricB = u;
barycentricC = v;
return t;
}
bool is_leaf(int node_index)
{
return nodes[node_index].element_index != -1;
}
bool TraceAnyHit(vec3 origin, float tmin, vec3 dir, float tmax)
{
if (tmax <= 0.0f)
return false;
RayBBox ray = create_ray(origin, origin + dir * tmax);
tmin /= tmax;
int stack[64];
int stackIndex = 0;
stack[stackIndex++] = nodesRoot;
do
{
int a = stack[--stackIndex];
if (overlap_bv_ray(ray, a))
{
if (is_leaf(a))
{
float baryB, baryC;
float t = intersect_triangle_ray(ray, a, baryB, baryC);
if (t >= tmin && t < 1.0)
{
return true;
}
}
else
{
stack[stackIndex++] = nodes[a].right;
stack[stackIndex++] = nodes[a].left;
}
}
} while (stackIndex > 0);
return false;
}
bool traceHit(vec3 origin, vec3 direction, float dist)
{
return TraceAnyHit(origin, 0.01f, direction, dist);
}
#endif
vec2 softshadow[9 * 3] = vec2[](
vec2( 0.0, 0.0),
vec2(-2.0,-2.0),