From 97073410c4fe5f4fef0624154f73b0b40e2abe1d Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Sun, 5 Jun 2022 23:24:53 +0200 Subject: [PATCH] Upload acceleration structures for the level mesh --- src/CMakeLists.txt | 2 + .../rendering/hwrenderer/data/hw_levelmesh.h | 21 + src/common/rendering/v_video.h | 2 + .../rendering/vulkan/renderer/vk_raytrace.cpp | 285 +++++++++++++ .../rendering/vulkan/renderer/vk_raytrace.h | 34 ++ .../vulkan/system/vk_framebuffer.cpp | 7 + .../rendering/vulkan/system/vk_framebuffer.h | 4 + src/g_levellocals.h | 2 + src/maploader/maploader.cpp | 1 + src/rendering/hwrenderer/doom_levelmesh.cpp | 387 ++++++++++++++++++ src/rendering/hwrenderer/doom_levelmesh.h | 55 +++ src/rendering/hwrenderer/hw_entrypoint.cpp | 2 + 12 files changed, 802 insertions(+) create mode 100644 src/common/rendering/hwrenderer/data/hw_levelmesh.h create mode 100644 src/common/rendering/vulkan/renderer/vk_raytrace.cpp create mode 100644 src/common/rendering/vulkan/renderer/vk_raytrace.h create mode 100644 src/rendering/hwrenderer/doom_levelmesh.cpp create mode 100644 src/rendering/hwrenderer/doom_levelmesh.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bd2a29013..28385adbc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -803,6 +803,7 @@ set (VULKAN_SOURCES common/rendering/vulkan/renderer/vk_streambuffer.cpp common/rendering/vulkan/renderer/vk_postprocess.cpp common/rendering/vulkan/renderer/vk_renderbuffers.cpp + common/rendering/vulkan/renderer/vk_raytrace.cpp common/rendering/vulkan/shaders/vk_shader.cpp common/rendering/vulkan/textures/vk_samplers.cpp common/rendering/vulkan/textures/vk_hwtexture.cpp @@ -961,6 +962,7 @@ set (PCH_SOURCES rendering/hwrenderer/hw_entrypoint.cpp rendering/hwrenderer/hw_vertexbuilder.cpp rendering/hwrenderer/doom_aabbtree.cpp + rendering/hwrenderer/doom_levelmesh.cpp rendering/hwrenderer/hw_models.cpp rendering/hwrenderer/hw_precache.cpp rendering/hwrenderer/scene/hw_lighting.cpp diff --git a/src/common/rendering/hwrenderer/data/hw_levelmesh.h b/src/common/rendering/hwrenderer/data/hw_levelmesh.h new file mode 100644 index 000000000..2626293e8 --- /dev/null +++ b/src/common/rendering/hwrenderer/data/hw_levelmesh.h @@ -0,0 +1,21 @@ + +#pragma once + +#include "tarray.h" +#include "vectors.h" + +namespace hwrenderer +{ + +class LevelMesh +{ +public: + virtual ~LevelMesh() = default; + + TArray MeshVertices; + TArray MeshUVIndex; + TArray MeshElements; + TArray MeshSurfaces; +}; + +} // namespace diff --git a/src/common/rendering/v_video.h b/src/common/rendering/v_video.h index a57ff455d..55b25c559 100644 --- a/src/common/rendering/v_video.h +++ b/src/common/rendering/v_video.h @@ -43,6 +43,7 @@ #include "v_2ddrawer.h" #include "intrect.h" #include "hw_shadowmap.h" +#include "hw_levelmesh.h" #include "buffers.h" @@ -164,6 +165,7 @@ public: { mShadowMap.SetAABBTree(tree); } + virtual void SetLevelMesh(hwrenderer::LevelMesh *mesh) { } bool allowSSBO() { #ifndef HW_BLOCK_SSBO diff --git a/src/common/rendering/vulkan/renderer/vk_raytrace.cpp b/src/common/rendering/vulkan/renderer/vk_raytrace.cpp new file mode 100644 index 000000000..36b14f372 --- /dev/null +++ b/src/common/rendering/vulkan/renderer/vk_raytrace.cpp @@ -0,0 +1,285 @@ +/* +** Vulkan backend +** Copyright (c) 2016-2020 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include "vk_raytrace.h" +#include "vulkan/system/vk_builders.h" +#include "vulkan/system/vk_framebuffer.h" +#include "doom_levelmesh.h" + +void VkRaytrace::SetLevelMesh(hwrenderer::LevelMesh* mesh) +{ + if (mesh != Mesh) + { + Reset(); + Mesh = mesh; + if (Mesh) + { + if (GetVulkanFrameBuffer()->device->SupportsDeviceExtension(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME) && GetVulkanFrameBuffer()->device->SupportsDeviceExtension(VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME)) + { + CreateVulkanObjects(); + } + else + { + Printf("This vulkan device does not support ray tracing."); + } + } + } +} + +void VkRaytrace::Reset() +{ + vertexBuffer.reset(); + indexBuffer.reset(); + transferBuffer.reset(); + + blScratchBuffer.reset(); + blAccelStructBuffer.reset(); + blAccelStruct.reset(); + + tlTransferBuffer.reset(); + tlScratchBuffer.reset(); + tlInstanceBuffer.reset(); + tlAccelStructBuffer.reset(); + tlAccelStruct.reset(); +} + +void VkRaytrace::CreateVulkanObjects() +{ + CreateVertexAndIndexBuffers(); + CreateBottomLevelAccelerationStructure(); + CreateTopLevelAccelerationStructure(); + + //CreateShaders(); + //CreatePipeline(); + //CreateDescriptorSet(); + + PipelineBarrier finishbuildbarrier; + finishbuildbarrier.addMemory(VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR, VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR); + finishbuildbarrier.execute(GetVulkanFrameBuffer()->GetTransferCommands(), VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR); +} + +void VkRaytrace::CreateVertexAndIndexBuffers() +{ + 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; + + BufferBuilder vbuilder; + vbuilder.setUsage(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); + vbuilder.setSize(vertexbuffersize); + vertexBuffer = vbuilder.create(GetVulkanFrameBuffer()->device); + vertexBuffer->SetDebugName("vertexBuffer"); + + BufferBuilder ibuilder; + ibuilder.setUsage(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); + ibuilder.setSize(indexbuffersize); + indexBuffer = ibuilder.create(GetVulkanFrameBuffer()->device); + indexBuffer->SetDebugName("indexBuffer"); + + BufferBuilder tbuilder; + tbuilder.setUsage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY); + tbuilder.setSize(transferbuffersize); + transferBuffer = tbuilder.create(GetVulkanFrameBuffer()->device); + transferBuffer->SetDebugName("transferBuffer"); + 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(); + + GetVulkanFrameBuffer()->GetTransferCommands()->copyBuffer(transferBuffer.get(), vertexBuffer.get(), vertexoffset); + GetVulkanFrameBuffer()->GetTransferCommands()->copyBuffer(transferBuffer.get(), indexBuffer.get(), indexoffset); + + VkMemoryBarrier barrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER }; + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR; + GetVulkanFrameBuffer()->GetTransferCommands()->pipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, 0, 1, &barrier, 0, nullptr, 0, nullptr); +} + +void VkRaytrace::CreateBottomLevelAccelerationStructure() +{ + VkBufferDeviceAddressInfo info = { VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO }; + info.buffer = vertexBuffer->buffer; + VkDeviceAddress vertexAddress = vkGetBufferDeviceAddress(GetVulkanFrameBuffer()->device->device, &info); + + info = { VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO }; + info.buffer = indexBuffer->buffer; + VkDeviceAddress indexAddress = vkGetBufferDeviceAddress(GetVulkanFrameBuffer()->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(FVector3); + 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(GetVulkanFrameBuffer()->device->device, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &buildInfo, &maxPrimitiveCount, &sizeInfo); + + BufferBuilder blbufbuilder; + blbufbuilder.setUsage(VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT); + blbufbuilder.setSize(sizeInfo.accelerationStructureSize); + blAccelStructBuffer = blbufbuilder.create(GetVulkanFrameBuffer()->device); + blAccelStructBuffer->SetDebugName("blAccelStructBuffer"); + + 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(GetVulkanFrameBuffer()->device->device, &createInfo, nullptr, &blAccelStructHandle); + if (result != VK_SUCCESS) + throw std::runtime_error("vkCreateAccelerationStructureKHR failed"); + blAccelStruct = std::make_unique(GetVulkanFrameBuffer()->device, blAccelStructHandle); + + BufferBuilder sbuilder; + sbuilder.setUsage(VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); + sbuilder.setSize(sizeInfo.buildScratchSize); + blScratchBuffer = sbuilder.create(GetVulkanFrameBuffer()->device); + blScratchBuffer->SetDebugName("blScratchBuffer"); + + info = { VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO }; + info.buffer = blScratchBuffer->buffer; + VkDeviceAddress scratchAddress = vkGetBufferDeviceAddress(GetVulkanFrameBuffer()->device->device, &info); + + buildInfo.dstAccelerationStructure = blAccelStruct->accelstruct; + buildInfo.scratchData.deviceAddress = scratchAddress; + VkAccelerationStructureBuildRangeInfoKHR* rangeInfos[] = { &rangeInfo }; + GetVulkanFrameBuffer()->GetTransferCommands()->buildAccelerationStructures(1, &buildInfo, rangeInfos); +} + +void VkRaytrace::CreateTopLevelAccelerationStructure() +{ + VkAccelerationStructureDeviceAddressInfoKHR addressInfo = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR }; + addressInfo.accelerationStructure = blAccelStruct->accelstruct; + VkDeviceAddress blAccelStructAddress = vkGetAccelerationStructureDeviceAddressKHR(GetVulkanFrameBuffer()->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; + + BufferBuilder tbuilder; + tbuilder.setUsage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY); + tbuilder.setSize(sizeof(VkAccelerationStructureInstanceKHR)); + tlTransferBuffer = tbuilder.create(GetVulkanFrameBuffer()->device); + tlTransferBuffer->SetDebugName("tlTransferBuffer"); + auto data = (uint8_t*)tlTransferBuffer->Map(0, sizeof(VkAccelerationStructureInstanceKHR)); + memcpy(data, &instance, sizeof(VkAccelerationStructureInstanceKHR)); + tlTransferBuffer->Unmap(); + + BufferBuilder instbufbuilder; + instbufbuilder.setUsage(VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | VK_BUFFER_USAGE_TRANSFER_DST_BIT); + instbufbuilder.setSize(sizeof(VkAccelerationStructureInstanceKHR)); + tlInstanceBuffer = instbufbuilder.create(GetVulkanFrameBuffer()->device); + tlInstanceBuffer->SetDebugName("tlInstanceBuffer"); + + GetVulkanFrameBuffer()->GetTransferCommands()->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; + GetVulkanFrameBuffer()->GetTransferCommands()->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(GetVulkanFrameBuffer()->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(GetVulkanFrameBuffer()->device->device, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &buildInfo, &maxInstanceCount, &sizeInfo); + + BufferBuilder tlbufbuilder; + tlbufbuilder.setUsage(VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT); + tlbufbuilder.setSize(sizeInfo.accelerationStructureSize); + tlAccelStructBuffer = tlbufbuilder.create(GetVulkanFrameBuffer()->device); + tlAccelStructBuffer->SetDebugName("tlAccelStructBuffer"); + + 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(GetVulkanFrameBuffer()->device->device, &createInfo, nullptr, &tlAccelStructHandle); + if (result != VK_SUCCESS) + throw std::runtime_error("vkCreateAccelerationStructureKHR failed"); + tlAccelStruct = std::make_unique(GetVulkanFrameBuffer()->device, tlAccelStructHandle); + + BufferBuilder sbuilder; + sbuilder.setUsage(VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); + sbuilder.setSize(sizeInfo.buildScratchSize); + tlScratchBuffer = sbuilder.create(GetVulkanFrameBuffer()->device); + tlScratchBuffer->SetDebugName("tlScratchBuffer"); + + info = { VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO }; + info.buffer = tlScratchBuffer->buffer; + VkDeviceAddress scratchAddress = vkGetBufferDeviceAddress(GetVulkanFrameBuffer()->device->device, &info); + + buildInfo.dstAccelerationStructure = tlAccelStruct->accelstruct; + buildInfo.scratchData.deviceAddress = scratchAddress; + + VkAccelerationStructureBuildRangeInfoKHR* rangeInfos[] = { &rangeInfo }; + GetVulkanFrameBuffer()->GetTransferCommands()->buildAccelerationStructures(1, &buildInfo, rangeInfos); +} diff --git a/src/common/rendering/vulkan/renderer/vk_raytrace.h b/src/common/rendering/vulkan/renderer/vk_raytrace.h new file mode 100644 index 000000000..3236db887 --- /dev/null +++ b/src/common/rendering/vulkan/renderer/vk_raytrace.h @@ -0,0 +1,34 @@ + +#pragma once + +#include "vulkan/system/vk_objects.h" +#include "hw_levelmesh.h" + +class VkRaytrace +{ +public: + void SetLevelMesh(hwrenderer::LevelMesh* mesh); + +private: + void Reset(); + void CreateVulkanObjects(); + void CreateVertexAndIndexBuffers(); + void CreateBottomLevelAccelerationStructure(); + void CreateTopLevelAccelerationStructure(); + + hwrenderer::LevelMesh* Mesh = nullptr; + + std::unique_ptr vertexBuffer; + std::unique_ptr indexBuffer; + std::unique_ptr transferBuffer; + + 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; +}; diff --git a/src/common/rendering/vulkan/system/vk_framebuffer.cpp b/src/common/rendering/vulkan/system/vk_framebuffer.cpp index b0a35399a..da2ac1736 100644 --- a/src/common/rendering/vulkan/system/vk_framebuffer.cpp +++ b/src/common/rendering/vulkan/system/vk_framebuffer.cpp @@ -49,6 +49,7 @@ #include "vulkan/renderer/vk_streambuffer.h" #include "vulkan/renderer/vk_postprocess.h" #include "vulkan/renderer/vk_renderbuffers.h" +#include "vulkan/renderer/vk_raytrace.h" #include "vulkan/shaders/vk_shader.h" #include "vulkan/textures/vk_samplers.h" #include "vulkan/textures/vk_hwtexture.h" @@ -153,6 +154,7 @@ void VulkanFrameBuffer::InitializeState() mPostprocess.reset(new VkPostprocess()); mRenderPassManager.reset(new VkRenderPassManager()); + mRaytrace.reset(new VkRaytrace()); mVertexData = new FFlatVertexBuffer(GetWidth(), GetHeight()); mSkyData = new FSkyVertexBuffer; @@ -801,6 +803,11 @@ void VulkanFrameBuffer::CreateFanToTrisIndexBuffer() FanToTrisIndexBuffer->SetData(sizeof(uint32_t) * data.Size(), data.Data(), BufferUsageType::Static); } +void VulkanFrameBuffer::SetLevelMesh(hwrenderer::LevelMesh* mesh) +{ + mRaytrace->SetLevelMesh(mesh); +} + void VulkanFrameBuffer::UpdateShadowMap() { mPostprocess->UpdateShadowMap(); diff --git a/src/common/rendering/vulkan/system/vk_framebuffer.h b/src/common/rendering/vulkan/system/vk_framebuffer.h index d234505cb..059af8eed 100644 --- a/src/common/rendering/vulkan/system/vk_framebuffer.h +++ b/src/common/rendering/vulkan/system/vk_framebuffer.h @@ -8,6 +8,7 @@ struct FRenderViewpoint; class VkSamplerManager; class VkShaderManager; class VkRenderPassManager; +class VkRaytrace; class VkRenderState; class VkStreamBuffer; class VKDataBuffer; @@ -32,6 +33,7 @@ public: VkShaderManager *GetShaderManager() { return mShaderManager.get(); } VkSamplerManager *GetSamplerManager() { return mSamplerManager.get(); } VkRenderPassManager *GetRenderPassManager() { return mRenderPassManager.get(); } + VkRaytrace* GetRaytrace() { return mRaytrace.get(); } VkRenderState *GetRenderState() { return mRenderState.get(); } VkPostprocess *GetPostprocess() { return mPostprocess.get(); } VkRenderBuffers *GetBuffers() { return mActiveRenderBuffers; } @@ -90,6 +92,7 @@ public: void PostProcessScene(bool swscene, int fixedcm, float flash, const std::function &afterBloomDrawEndScene2D) override; void AmbientOccludeScene(float m5) override; void SetSceneRenderTarget(bool useSSAO) override; + void SetLevelMesh(hwrenderer::LevelMesh* mesh) override; void UpdateShadowMap() override; void SetSaveBuffers(bool yes) override; void ImageTransitionScene(bool unknown) override; @@ -133,6 +136,7 @@ private: std::unique_ptr mSaveBuffers; std::unique_ptr mPostprocess; std::unique_ptr mRenderPassManager; + std::unique_ptr mRaytrace; std::unique_ptr mCommandPool; std::unique_ptr mTransferCommands; std::unique_ptr mRenderState; diff --git a/src/g_levellocals.h b/src/g_levellocals.h index 09a2df310..9953a3e7d 100644 --- a/src/g_levellocals.h +++ b/src/g_levellocals.h @@ -55,6 +55,7 @@ #include "r_data/r_canvastexture.h" #include "r_data/r_interpolate.h" #include "doom_aabbtree.h" +#include "doom_levelmesh.h" //============================================================================ // @@ -476,6 +477,7 @@ public: FCanvasTextureInfo canvasTextureInfo; EventManager *localEventManager = nullptr; DoomLevelAABBTree* aabbTree = nullptr; + DoomLevelMesh* levelMesh = nullptr; // [ZZ] Destructible geometry information TMap healthGroups; diff --git a/src/maploader/maploader.cpp b/src/maploader/maploader.cpp index 6060ad6e0..9e4126196 100644 --- a/src/maploader/maploader.cpp +++ b/src/maploader/maploader.cpp @@ -3266,6 +3266,7 @@ void MapLoader::LoadLevel(MapData *map, const char *lumpname, int position) Level->FinalizePortals(); // finalize line portals after polyobjects have been initialized. This info is needed for properly flagging them. Level->aabbTree = new DoomLevelAABBTree(Level); + Level->levelMesh = new DoomLevelMesh(*Level); } //========================================================================== diff --git a/src/rendering/hwrenderer/doom_levelmesh.cpp b/src/rendering/hwrenderer/doom_levelmesh.cpp new file mode 100644 index 000000000..ea8aa314d --- /dev/null +++ b/src/rendering/hwrenderer/doom_levelmesh.cpp @@ -0,0 +1,387 @@ + +#include "templates.h" +#include "doom_levelmesh.h" +#include "g_levellocals.h" +#include "texturemanager.h" + +DoomLevelMesh::DoomLevelMesh(FLevelLocals &doomMap) +{ + for (unsigned int i = 0; i < doomMap.sides.Size(); i++) + { + CreateSideSurfaces(doomMap, &doomMap.sides[i]); + } + + CreateSubsectorSurfaces(doomMap); + + for (size_t i = 0; i < Surfaces.Size(); i++) + { + const Surface &s = Surfaces[i]; + int numVerts = s.numVerts; + unsigned int pos = s.startVertIndex; + FVector3* verts = &MeshVertices[pos]; + + for (int j = 0; j < numVerts; j++) + { + MeshUVIndex.Push(j); + } + + if (s.type == ST_FLOOR || s.type == ST_CEILING) + { + for (int j = 2; j < numVerts; j++) + { + if (!IsDegenerate(verts[0], verts[j - 1], verts[j])) + { + MeshElements.Push(pos); + MeshElements.Push(pos + j - 1); + MeshElements.Push(pos + j); + MeshSurfaces.Push((int)i); + } + } + } + else if (s.type == ST_MIDDLEWALL || s.type == ST_UPPERWALL || s.type == ST_LOWERWALL) + { + if (!IsDegenerate(verts[0], verts[1], verts[2])) + { + MeshElements.Push(pos + 0); + MeshElements.Push(pos + 1); + MeshElements.Push(pos + 2); + MeshSurfaces.Push((int)i); + } + if (!IsDegenerate(verts[1], verts[2], verts[3])) + { + MeshElements.Push(pos + 3); + MeshElements.Push(pos + 2); + MeshElements.Push(pos + 1); + MeshSurfaces.Push((int)i); + } + } + } +} + +void DoomLevelMesh::CreateSideSurfaces(FLevelLocals &doomMap, side_t *side) +{ + sector_t *front; + sector_t *back; + + front = side->sector; + back = (side->linedef->frontsector == front) ? side->linedef->backsector : side->linedef->frontsector; + + if (IsControlSector(front)) + return; + + FVector2 v1 = ToFVector2(side->V1()->fPos()); + FVector2 v2 = ToFVector2(side->V2()->fPos()); + + float v1Top = (float)front->ceilingplane.ZatPoint(v1); + float v1Bottom = (float)front->floorplane.ZatPoint(v1); + float v2Top = (float)front->ceilingplane.ZatPoint(v2); + float v2Bottom = (float)front->floorplane.ZatPoint(v2); + + int typeIndex = side->Index(); + + FVector2 dx(v2.X - v1.X, v2.Y - v1.Y); + float distance = dx.Length(); + + if (back) + { + for (unsigned int j = 0; j < front->e->XFloor.ffloors.Size(); j++) + { + F3DFloor *xfloor = front->e->XFloor.ffloors[j]; + + // Don't create a line when both sectors have the same 3d floor + bool bothSides = false; + for (unsigned int k = 0; k < back->e->XFloor.ffloors.Size(); k++) + { + if (back->e->XFloor.ffloors[k] == xfloor) + { + bothSides = true; + break; + } + } + if (bothSides) + continue; + + Surface surf; + surf.type = ST_MIDDLEWALL; + surf.typeIndex = typeIndex; + surf.controlSector = xfloor->model; + + FVector3 verts[4]; + verts[0].X = verts[2].X = v2.X; + verts[0].Y = verts[2].Y = v2.Y; + verts[1].X = verts[3].X = v1.X; + verts[1].Y = verts[3].Y = v1.Y; + verts[0].Z = (float)xfloor->model->floorplane.ZatPoint(v2); + verts[1].Z = (float)xfloor->model->floorplane.ZatPoint(v1); + verts[2].Z = (float)xfloor->model->ceilingplane.ZatPoint(v2); + verts[3].Z = (float)xfloor->model->ceilingplane.ZatPoint(v1); + + surf.startVertIndex = MeshVertices.Size(); + surf.numVerts = 4; + MeshVertices.Push(verts[0]); + MeshVertices.Push(verts[1]); + MeshVertices.Push(verts[2]); + MeshVertices.Push(verts[3]); + + surf.plane = ToPlane(verts[0], verts[1], verts[2]); + Surfaces.Push(surf); + } + + float v1TopBack = (float)back->ceilingplane.ZatPoint(v1); + float v1BottomBack = (float)back->floorplane.ZatPoint(v1); + float v2TopBack = (float)back->ceilingplane.ZatPoint(v2); + float v2BottomBack = (float)back->floorplane.ZatPoint(v2); + + if (v1Top == v1TopBack && v1Bottom == v1BottomBack && v2Top == v2TopBack && v2Bottom == v2BottomBack) + { + return; + } + + // bottom seg + if (v1Bottom < v1BottomBack || v2Bottom < v2BottomBack) + { + if (IsBottomSideVisible(side)) + { + Surface surf; + + FVector3 verts[4]; + verts[0].X = verts[2].X = v1.X; + verts[0].Y = verts[2].Y = v1.Y; + verts[1].X = verts[3].X = v2.X; + verts[1].Y = verts[3].Y = v2.Y; + verts[0].Z = v1Bottom; + verts[1].Z = v2Bottom; + verts[2].Z = v1BottomBack; + verts[3].Z = v2BottomBack; + + surf.startVertIndex = MeshVertices.Size(); + surf.numVerts = 4; + MeshVertices.Push(verts[0]); + MeshVertices.Push(verts[1]); + MeshVertices.Push(verts[2]); + MeshVertices.Push(verts[3]); + + surf.plane = ToPlane(verts[0], verts[1], verts[2]); + surf.type = ST_LOWERWALL; + surf.typeIndex = typeIndex; + surf.controlSector = nullptr; + + Surfaces.Push(surf); + } + + v1Bottom = v1BottomBack; + v2Bottom = v2BottomBack; + } + + // top seg + if (v1Top > v1TopBack || v2Top > v2TopBack) + { + bool bSky = IsTopSideSky(front, back, side); + if (bSky || IsTopSideVisible(side)) + { + Surface surf; + + FVector3 verts[4]; + verts[0].X = verts[2].X = v1.X; + verts[0].Y = verts[2].Y = v1.Y; + verts[1].X = verts[3].X = v2.X; + verts[1].Y = verts[3].Y = v2.Y; + verts[0].Z = v1TopBack; + verts[1].Z = v2TopBack; + verts[2].Z = v1Top; + verts[3].Z = v2Top; + + surf.startVertIndex = MeshVertices.Size(); + surf.numVerts = 4; + MeshVertices.Push(verts[0]); + MeshVertices.Push(verts[1]); + MeshVertices.Push(verts[2]); + MeshVertices.Push(verts[3]); + + surf.plane = ToPlane(verts[0], verts[1], verts[2]); + surf.type = ST_UPPERWALL; + surf.typeIndex = typeIndex; + surf.bSky = bSky; + surf.controlSector = nullptr; + + Surfaces.Push(surf); + } + + v1Top = v1TopBack; + v2Top = v2TopBack; + } + } + + // middle seg + if (back == nullptr) + { + Surface surf; + + FVector3 verts[4]; + verts[0].X = verts[2].X = v1.X; + verts[0].Y = verts[2].Y = v1.Y; + verts[1].X = verts[3].X = v2.X; + verts[1].Y = verts[3].Y = v2.Y; + verts[0].Z = v1Bottom; + verts[1].Z = v2Bottom; + verts[2].Z = v1Top; + verts[3].Z = v2Top; + + surf.startVertIndex = MeshVertices.Size(); + surf.numVerts = 4; + MeshVertices.Push(verts[0]); + MeshVertices.Push(verts[1]); + MeshVertices.Push(verts[2]); + MeshVertices.Push(verts[3]); + + surf.plane = ToPlane(verts[0], verts[1], verts[2]); + surf.type = ST_MIDDLEWALL; + surf.typeIndex = typeIndex; + surf.controlSector = nullptr; + + Surfaces.Push(surf); + } +} + +void DoomLevelMesh::CreateFloorSurface(FLevelLocals &doomMap, subsector_t *sub, sector_t *sector, int typeIndex, bool is3DFloor) +{ + Surface surf; + + if (!is3DFloor) + { + surf.plane = sector->floorplane; + } + else + { + surf.plane = sector->ceilingplane; + surf.plane.FlipVert(); + } + + surf.numVerts = sub->numlines; + surf.startVertIndex = MeshVertices.Size(); + MeshVertices.Resize(surf.startVertIndex + surf.numVerts); + FVector3* verts = &MeshVertices[surf.startVertIndex]; + + for (int j = 0; j < surf.numVerts; j++) + { + seg_t *seg = &sub->firstline[(surf.numVerts - 1) - j]; + FVector2 v1 = ToFVector2(seg->v1->fPos()); + + verts[j].X = v1.X; + verts[j].Y = v1.Y; + verts[j].Z = (float)surf.plane.ZatPoint(verts[j]); + } + + surf.type = ST_FLOOR; + surf.typeIndex = typeIndex; + surf.controlSector = is3DFloor ? sector : nullptr; + + Surfaces.Push(surf); +} + +void DoomLevelMesh::CreateCeilingSurface(FLevelLocals &doomMap, subsector_t *sub, sector_t *sector, int typeIndex, bool is3DFloor) +{ + Surface surf; + surf.bSky = IsSkySector(sector); + + if (!is3DFloor) + { + surf.plane = sector->ceilingplane; + } + else + { + surf.plane = sector->floorplane; + surf.plane.FlipVert(); + } + + surf.numVerts = sub->numlines; + surf.startVertIndex = MeshVertices.Size(); + MeshVertices.Resize(surf.startVertIndex + surf.numVerts); + FVector3* verts = &MeshVertices[surf.startVertIndex]; + + for (int j = 0; j < surf.numVerts; j++) + { + seg_t *seg = &sub->firstline[j]; + FVector2 v1 = ToFVector2(seg->v1->fPos()); + + verts[j].X = v1.X; + verts[j].Y = v1.Y; + verts[j].Z = (float)surf.plane.ZatPoint(verts[j]); + } + + surf.type = ST_CEILING; + surf.typeIndex = typeIndex; + surf.controlSector = is3DFloor ? sector : nullptr; + + Surfaces.Push(surf); +} + +void DoomLevelMesh::CreateSubsectorSurfaces(FLevelLocals &doomMap) +{ + for (unsigned int i = 0; i < doomMap.subsectors.Size(); i++) + { + subsector_t *sub = &doomMap.subsectors[i]; + + if (sub->numlines < 3) + { + continue; + } + + sector_t *sector = sub->sector; + if (!sector || IsControlSector(sector)) + continue; + + CreateFloorSurface(doomMap, sub, sector, i, false); + CreateCeilingSurface(doomMap, sub, sector, i, false); + + for (unsigned int j = 0; j < sector->e->XFloor.ffloors.Size(); j++) + { + CreateFloorSurface(doomMap, sub, sector->e->XFloor.ffloors[j]->model, i, true); + CreateCeilingSurface(doomMap, sub, sector->e->XFloor.ffloors[j]->model, i, true); + } + } +} + +bool DoomLevelMesh::IsTopSideSky(sector_t* frontsector, sector_t* backsector, side_t* side) +{ + return IsSkySector(frontsector) && IsSkySector(backsector); +} + +bool DoomLevelMesh::IsTopSideVisible(side_t* side) +{ + auto tex = TexMan.GetGameTexture(side->GetTexture(side_t::top), true); + return tex && tex->isValid(); +} + +bool DoomLevelMesh::IsBottomSideVisible(side_t* side) +{ + auto tex = TexMan.GetGameTexture(side->GetTexture(side_t::bottom), true); + return tex && tex->isValid(); +} + +bool DoomLevelMesh::IsSkySector(sector_t* sector) +{ + return sector->GetTexture(sector_t::ceiling) == skyflatnum; +} + +bool DoomLevelMesh::IsControlSector(sector_t* sector) +{ + //return sector->controlsector; + return false; +} + +bool DoomLevelMesh::IsDegenerate(const FVector3 &v0, const FVector3 &v1, const FVector3 &v2) +{ + // A degenerate triangle has a zero cross product for two of its sides. + float ax = v1.X - v0.X; + float ay = v1.Y - v0.Y; + float az = v1.Z - v0.Z; + float bx = v2.X - v0.X; + float by = v2.Y - v0.Y; + float bz = v2.Z - v0.Z; + float crossx = ay * bz - az * by; + float crossy = az * bx - ax * bz; + float crossz = ax * by - ay * bx; + float crosslengthsqr = crossx * crossx + crossy * crossy + crossz * crossz; + return crosslengthsqr <= 1.e-6f; +} diff --git a/src/rendering/hwrenderer/doom_levelmesh.h b/src/rendering/hwrenderer/doom_levelmesh.h new file mode 100644 index 000000000..73dc1a5bf --- /dev/null +++ b/src/rendering/hwrenderer/doom_levelmesh.h @@ -0,0 +1,55 @@ + +#pragma once + +#include "hw_levelmesh.h" +#include "tarray.h" +#include "vectors.h" +#include "r_defs.h" + +struct FLevelLocals; + +struct Surface +{ + SurfaceType type; + int typeIndex; + int numVerts; + unsigned int startVertIndex; + secplane_t plane; + sector_t *controlSector; + bool bSky; +}; + +class DoomLevelMesh : public hwrenderer::LevelMesh +{ +public: + DoomLevelMesh(FLevelLocals &doomMap); + + TArray Surfaces; + +private: + void CreateSubsectorSurfaces(FLevelLocals &doomMap); + void CreateCeilingSurface(FLevelLocals &doomMap, subsector_t *sub, sector_t *sector, int typeIndex, bool is3DFloor); + void CreateFloorSurface(FLevelLocals &doomMap, subsector_t *sub, sector_t *sector, int typeIndex, bool is3DFloor); + void CreateSideSurfaces(FLevelLocals &doomMap, side_t *side); + + static bool IsTopSideSky(sector_t* frontsector, sector_t* backsector, side_t* side); + static bool IsTopSideVisible(side_t* side); + static bool IsBottomSideVisible(side_t* side); + static bool IsSkySector(sector_t* sector); + static bool IsControlSector(sector_t* sector); + + static secplane_t ToPlane(const FVector3& pt1, const FVector3& pt2, const FVector3& pt3) + { + FVector3 n = ((pt2 - pt1) ^ (pt3 - pt2)).Unit(); + float d = pt1 | n; + secplane_t p; + p.set(n.X, n.Y, n.Z, d); + return p; + } + + static FVector2 ToFVector2(const DVector2& v) { return FVector2((float)v.X, (float)v.Y); } + static FVector3 ToFVector3(const DVector3& v) { return FVector3((float)v.X, (float)v.Y, (float)v.Z); } + static FVector4 ToFVector4(const DVector4& v) { return FVector4((float)v.X, (float)v.Y, (float)v.Z, (float)v.W); } + + static bool IsDegenerate(const FVector3 &v0, const FVector3 &v1, const FVector3 &v2); +}; diff --git a/src/rendering/hwrenderer/hw_entrypoint.cpp b/src/rendering/hwrenderer/hw_entrypoint.cpp index b3ef82afd..d3d409356 100644 --- a/src/rendering/hwrenderer/hw_entrypoint.cpp +++ b/src/rendering/hwrenderer/hw_entrypoint.cpp @@ -123,6 +123,8 @@ sector_t* RenderViewpoint(FRenderViewpoint& mainvp, AActor* camera, IntRect* bou screen->mShadowMap.SetCollectLights(nullptr); } + screen->SetLevelMesh(camera->Level->levelMesh); + // Update the attenuation flag of all light defaults for each viewpoint. // This function will only do something if the setting differs. FLightDefaults::SetAttenuationForLevel(!!(camera->Level->flags3 & LEVEL3_ATTENUATE));