/* ** 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_levelmesh.h" #include "zvulkan/vulkanbuilders.h" #include "vulkan/vk_renderdevice.h" #include "vulkan/commands/vk_commandbuffer.h" #include "vulkan/descriptorsets/vk_descriptorset.h" #include "vulkan/pipelines/vk_renderpass.h" #include "hw_levelmesh.h" #include "hw_material.h" #include "texturemanager.h" #include "cmdlib.h" VkLevelMesh::VkLevelMesh(VulkanRenderDevice* fb) : fb(fb) { useRayQuery = fb->IsRayQueryEnabled(); CreateViewerObjects(); SetLevelMesh(nullptr); } void VkLevelMesh::SetLevelMesh(LevelMesh* mesh) { if (!mesh) mesh = &NullMesh; Mesh = mesh; CreateVulkanObjects(); } void VkLevelMesh::Reset() { auto deletelist = fb->GetCommands()->DrawDeleteList.get(); deletelist->Add(std::move(VertexBuffer)); deletelist->Add(std::move(UniformIndexBuffer)); deletelist->Add(std::move(IndexBuffer)); deletelist->Add(std::move(NodeBuffer)); deletelist->Add(std::move(SurfaceBuffer)); deletelist->Add(std::move(UniformsBuffer)); deletelist->Add(std::move(SurfaceIndexBuffer)); deletelist->Add(std::move(PortalBuffer)); deletelist->Add(std::move(LightBuffer)); deletelist->Add(std::move(LightIndexBuffer)); deletelist->Add(std::move(DrawIndexBuffer)); for (BLAS& blas : DynamicBLAS) { deletelist->Add(std::move(blas.ScratchBuffer)); deletelist->Add(std::move(blas.AccelStructBuffer)); deletelist->Add(std::move(blas.AccelStruct)); } DynamicBLAS.clear(); deletelist->Add(std::move(TopLevelAS.TransferBuffer)); deletelist->Add(std::move(TopLevelAS.InstanceBuffer)); deletelist->Add(std::move(TopLevelAS.ScratchBuffer)); deletelist->Add(std::move(TopLevelAS.AccelStructBuffer)); deletelist->Add(std::move(TopLevelAS.AccelStruct)); } void VkLevelMesh::CreateVulkanObjects() { Reset(); CreateBuffers(); UploadMeshes(); if (useRayQuery) { // Wait for uploads to finish PipelineBarrier() .AddMemory(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR | VK_ACCESS_SHADER_READ_BIT) .Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); // Find out how many segments we should split the map into DynamicBLAS.resize(32); // fb->GetDevice()->PhysicalDevice.Properties.AccelerationStructure.maxInstanceCount is 65 or 382 on current devices (aug 2024) IndexesPerBLAS = ((Mesh->Mesh.Indexes.size() + 2) / 3 / DynamicBLAS.size() + 1) * 3; InstanceCount = (Mesh->Mesh.IndexCount + IndexesPerBLAS - 1) / IndexesPerBLAS; // Create a BLAS for each segment in use for (int instance = 0; instance < InstanceCount; instance++) { int indexStart = instance * IndexesPerBLAS; int indexEnd = std::min(indexStart + IndexesPerBLAS, Mesh->Mesh.IndexCount); DynamicBLAS[instance] = CreateBLAS(true, indexStart, indexEnd - indexStart); } CreateTLASInstanceBuffer(); UploadTLASInstanceBuffer(); // Wait for bottom level builds to finish before using it as input to a toplevel accel structure. Also wait for the instance buffer upload to complete. PipelineBarrier() .AddMemory(VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR | VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR | VK_ACCESS_SHADER_READ_BIT) .Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR); CreateTopLevelAS(InstanceCount); // Finish building the accel struct before using it from the shaders PipelineBarrier() .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); } else { // Uploads must finish before we can read from the shaders PipelineBarrier() .AddMemory(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT) .Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); } } void VkLevelMesh::BeginFrame() { bool accelStructNeedsUpdate = false; if (useRayQuery) { InstanceCount = (Mesh->Mesh.IndexCount + IndexesPerBLAS - 1) / IndexesPerBLAS; accelStructNeedsUpdate = Mesh->UploadRanges.Index.Size() != 0; for (const MeshBufferRange& range : Mesh->UploadRanges.Index) { int start = range.Start / IndexesPerBLAS; int end = (range.End + IndexesPerBLAS - 1) / IndexesPerBLAS; for (int i = start; i < end; i++) { DynamicBLAS[i].NeedsUpdate = true; } } } UploadMeshes(); if (useRayQuery) { // Wait for uploads to finish PipelineBarrier() .AddMemory(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR | VK_ACCESS_SHADER_READ_BIT) .Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); if (accelStructNeedsUpdate) { // To do: we should reuse the buffers. // Create a new BLAS for each segment that changed auto deletelist = fb->GetCommands()->DrawDeleteList.get(); for (int instance = 0; instance < InstanceCount; instance++) { BLAS& blas = DynamicBLAS[instance]; if (blas.NeedsUpdate) { deletelist->Add(std::move(blas.ScratchBuffer)); deletelist->Add(std::move(blas.AccelStructBuffer)); deletelist->Add(std::move(blas.AccelStruct)); int indexStart = instance * IndexesPerBLAS; int indexEnd = std::min(indexStart + IndexesPerBLAS, Mesh->Mesh.IndexCount); blas = CreateBLAS(true, indexStart, indexEnd - indexStart); } } deletelist->Add(std::move(TopLevelAS.TransferBuffer)); deletelist->Add(std::move(TopLevelAS.InstanceBuffer)); CreateTLASInstanceBuffer(); UploadTLASInstanceBuffer(); } // Wait for bottom level builds to finish before using it as input to a toplevel accel structure. Also wait for the instance buffer upload to complete. PipelineBarrier() .AddMemory(VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR | VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR | VK_ACCESS_SHADER_READ_BIT) .Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR); UpdateTopLevelAS(InstanceCount); // Finish building the accel struct before using it from the shaders PipelineBarrier() .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); } else { // Uploads must finish before we can read from the shaders PipelineBarrier() .AddMemory(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT) .Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); } WriteDescriptors write; write.AddBuffer(Viewer.DescriptorSet.get(), 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, GetSurfaceIndexBuffer()); write.AddBuffer(Viewer.DescriptorSet.get(), 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, GetSurfaceBuffer()); write.AddBuffer(Viewer.DescriptorSet.get(), 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, GetLightBuffer()); write.AddBuffer(Viewer.DescriptorSet.get(), 3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, GetLightIndexBuffer()); write.AddBuffer(Viewer.DescriptorSet.get(), 4, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, GetPortalBuffer()); if (useRayQuery) { write.AddAccelerationStructure(Viewer.DescriptorSet.get(), 5, GetAccelStruct()); } else { write.AddBuffer(Viewer.DescriptorSet.get(), 5, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, GetNodeBuffer()); } write.AddBuffer(Viewer.DescriptorSet.get(), 6, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, GetVertexBuffer()); write.AddBuffer(Viewer.DescriptorSet.get(), 7, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, GetIndexBuffer()); write.Execute(fb->GetDevice()); } void VkLevelMesh::UploadMeshes() { VkLevelMeshUploader uploader(this); uploader.Upload(); } void VkLevelMesh::CreateBuffers() { VertexBuffer = BufferBuilder() .Usage( VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | (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(Mesh->Mesh.Vertices.Size() * sizeof(FFlatVertex)) .DebugName("VertexBuffer") .Create(fb->GetDevice()); UniformIndexBuffer = BufferBuilder() .Usage( VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT) .Size(Mesh->Mesh.UniformIndexes.size() * sizeof(int)) .DebugName("UniformIndexes") .Create(fb->GetDevice()); IndexBuffer = BufferBuilder() .Usage( VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | (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->Mesh.Indexes.Size() * sizeof(uint32_t)) .DebugName("IndexBuffer") .Create(fb->GetDevice()); DrawIndexBuffer = BufferBuilder() .Usage(VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT) .Size((size_t)Mesh->Mesh.DrawIndexes.Size() * sizeof(uint32_t)) .DebugName("DrawIndexBuffer") .Create(fb->GetDevice()); NodeBuffer = BufferBuilder() .Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT) .Size(sizeof(CollisionNodeBufferHeader) + Mesh->Mesh.MaxNodes * sizeof(CollisionNode)) .DebugName("NodeBuffer") .Create(fb->GetDevice()); SurfaceIndexBuffer = BufferBuilder() .Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT) .Size(Mesh->Mesh.SurfaceIndexes.Size() * sizeof(int)) .DebugName("SurfaceBuffer") .Create(fb->GetDevice()); SurfaceBuffer = BufferBuilder() .Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT) .Size(Mesh->Mesh.Surfaces.Size() * sizeof(SurfaceInfo)) .DebugName("SurfaceBuffer") .Create(fb->GetDevice()); UniformsBuffer = BufferBuilder() .Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT) .Size(Mesh->Mesh.Uniforms.Size() * sizeof(SurfaceUniforms)) .DebugName("SurfaceUniformsBuffer") .Create(fb->GetDevice()); PortalBuffer = BufferBuilder() .Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT) .Size(Mesh->Portals.Size() * sizeof(PortalInfo)) .DebugName("PortalBuffer") .Create(fb->GetDevice()); LightBuffer = BufferBuilder() .Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT) .Size(Mesh->Mesh.Lights.Size() * sizeof(LightInfo)) .DebugName("LightBuffer") .Create(fb->GetDevice()); LightIndexBuffer = BufferBuilder() .Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT) .Size(Mesh->Mesh.LightIndexes.Size() * sizeof(int32_t)) .DebugName("LightIndexBuffer") .Create(fb->GetDevice()); } VkLevelMesh::BLAS VkLevelMesh::CreateBLAS(bool preferFastBuild, int indexOffset, int indexCount) { BLAS blas; VkAccelerationStructureBuildGeometryInfoKHR buildInfo = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR }; VkAccelerationStructureGeometryKHR accelStructBLDesc = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR }; VkAccelerationStructureGeometryKHR* geometries[] = { &accelStructBLDesc }; 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_R32G32B32A32_SFLOAT; accelStructBLDesc.geometry.triangles.vertexData.deviceAddress = VertexBuffer->GetDeviceAddress(); accelStructBLDesc.geometry.triangles.vertexStride = sizeof(FFlatVertex); accelStructBLDesc.geometry.triangles.indexType = VK_INDEX_TYPE_UINT32; accelStructBLDesc.geometry.triangles.indexData.deviceAddress = IndexBuffer->GetDeviceAddress() + indexOffset * sizeof(uint32_t); accelStructBLDesc.geometry.triangles.maxVertex = Mesh->Mesh.Vertices.Size() - 1; buildInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR; buildInfo.flags = preferFastBuild ? VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_BUILD_BIT_KHR : VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR; buildInfo.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR; buildInfo.geometryCount = 1; buildInfo.ppGeometries = geometries; uint32_t maxPrimitiveCount = indexCount / 3; VkAccelerationStructureBuildSizesInfoKHR sizeInfo = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR }; vkGetAccelerationStructureBuildSizesKHR(fb->GetDevice()->device, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &buildInfo, &maxPrimitiveCount, &sizeInfo); blas.AccelStructBuffer = BufferBuilder() .Usage(VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT) .Size(sizeInfo.accelerationStructureSize) .DebugName("BLAS.AccelStructBuffer") .Create(fb->GetDevice()); blas.AccelStruct = AccelerationStructureBuilder() .Type(VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR) .Buffer(blas.AccelStructBuffer.get(), sizeInfo.accelerationStructureSize) .DebugName("BLAS.AccelStruct") .Create(fb->GetDevice()); blas.ScratchBuffer = BufferBuilder() .Usage(VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) .Size(sizeInfo.buildScratchSize) .MinAlignment(fb->GetDevice()->PhysicalDevice.Properties.AccelerationStructure.minAccelerationStructureScratchOffsetAlignment) .DebugName("BLAS.ScratchBuffer") .Create(fb->GetDevice()); blas.DeviceAddress = blas.AccelStruct->GetDeviceAddress(); blas.InstanceCustomIndex = indexOffset / 3; buildInfo.dstAccelerationStructure = blas.AccelStruct->accelstruct; buildInfo.scratchData.deviceAddress = blas.ScratchBuffer->GetDeviceAddress(); VkAccelerationStructureBuildRangeInfoKHR rangeInfo = {}; VkAccelerationStructureBuildRangeInfoKHR* rangeInfos[] = { &rangeInfo }; rangeInfo.primitiveCount = maxPrimitiveCount; fb->GetCommands()->GetTransferCommands()->buildAccelerationStructures(1, &buildInfo, rangeInfos); return blas; } void VkLevelMesh::CreateTLASInstanceBuffer() { TopLevelAS.TransferBuffer = BufferBuilder() .Usage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY) .Size(sizeof(VkAccelerationStructureInstanceKHR) * DynamicBLAS.size()) .DebugName("TopLevelAS.TransferBuffer") .Create(fb->GetDevice()); TopLevelAS.InstanceBuffer = 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) * DynamicBLAS.size()) .DebugName("TopLevelAS.InstanceBuffer") .Create(fb->GetDevice()); } void VkLevelMesh::CreateTopLevelAS(int instanceCount) { VkAccelerationStructureBuildGeometryInfoKHR buildInfo = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR }; VkAccelerationStructureGeometryKHR accelStructTLDesc = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR }; VkAccelerationStructureGeometryKHR* geometries[] = { &accelStructTLDesc }; buildInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR; buildInfo.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR; buildInfo.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR; buildInfo.geometryCount = 1; buildInfo.ppGeometries = geometries; accelStructTLDesc.geometryType = VK_GEOMETRY_TYPE_INSTANCES_KHR; accelStructTLDesc.geometry.instances = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_INSTANCES_DATA_KHR }; accelStructTLDesc.geometry.instances.data.deviceAddress = TopLevelAS.InstanceBuffer->GetDeviceAddress(); uint32_t maxInstanceCount = (uint32_t)DynamicBLAS.size(); VkAccelerationStructureBuildSizesInfoKHR sizeInfo = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR }; vkGetAccelerationStructureBuildSizesKHR(fb->GetDevice()->device, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &buildInfo, &maxInstanceCount, &sizeInfo); TopLevelAS.AccelStructBuffer = BufferBuilder() .Usage(VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT) .Size(sizeInfo.accelerationStructureSize) .DebugName("TopLevelAS.AccelStructBuffer") .Create(fb->GetDevice()); TopLevelAS.AccelStruct = AccelerationStructureBuilder() .Type(VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR) .Buffer(TopLevelAS.AccelStructBuffer.get(), sizeInfo.accelerationStructureSize) .DebugName("TopLevelAS.AccelStruct") .Create(fb->GetDevice()); TopLevelAS.ScratchBuffer = BufferBuilder() .Usage(VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) .Size(sizeInfo.buildScratchSize) .MinAlignment(fb->GetDevice()->PhysicalDevice.Properties.AccelerationStructure.minAccelerationStructureScratchOffsetAlignment) .DebugName("TopLevelAS.ScratchBuffer") .Create(fb->GetDevice()); buildInfo.dstAccelerationStructure = TopLevelAS.AccelStruct->accelstruct; buildInfo.scratchData.deviceAddress = TopLevelAS.ScratchBuffer->GetDeviceAddress(); VkAccelerationStructureBuildRangeInfoKHR rangeInfo = {}; VkAccelerationStructureBuildRangeInfoKHR* rangeInfos[] = { &rangeInfo }; rangeInfo.primitiveCount = instanceCount; fb->GetCommands()->GetTransferCommands()->buildAccelerationStructures(1, &buildInfo, rangeInfos); } void VkLevelMesh::UpdateTopLevelAS(int instanceCount) { VkAccelerationStructureBuildGeometryInfoKHR buildInfo = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR }; VkAccelerationStructureGeometryKHR accelStructTLDesc = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR }; VkAccelerationStructureGeometryKHR* geometries[] = { &accelStructTLDesc }; buildInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR; buildInfo.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR; buildInfo.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR; buildInfo.geometryCount = 1; buildInfo.ppGeometries = geometries; accelStructTLDesc.geometryType = VK_GEOMETRY_TYPE_INSTANCES_KHR; accelStructTLDesc.geometry.instances = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_INSTANCES_DATA_KHR }; accelStructTLDesc.geometry.instances.data.deviceAddress = TopLevelAS.InstanceBuffer->GetDeviceAddress(); buildInfo.dstAccelerationStructure = TopLevelAS.AccelStruct->accelstruct; buildInfo.scratchData.deviceAddress = TopLevelAS.ScratchBuffer->GetDeviceAddress(); VkAccelerationStructureBuildRangeInfoKHR rangeInfo = {}; VkAccelerationStructureBuildRangeInfoKHR* rangeInfos[] = { &rangeInfo }; rangeInfo.primitiveCount = instanceCount; fb->GetCommands()->GetTransferCommands()->buildAccelerationStructures(1, &buildInfo, rangeInfos); } void VkLevelMesh::UploadTLASInstanceBuffer() { VkAccelerationStructureInstanceKHR instance = {}; instance.transform.matrix[0][0] = 1.0f; instance.transform.matrix[1][1] = 1.0f; instance.transform.matrix[2][2] = 1.0f; instance.mask = 0xff; instance.flags = 0; auto data = (uint8_t*)TopLevelAS.TransferBuffer->Map(0, sizeof(VkAccelerationStructureInstanceKHR) * DynamicBLAS.size()); for (BLAS& blas : DynamicBLAS) { instance.instanceCustomIndex = blas.InstanceCustomIndex; instance.accelerationStructureReference = blas.DeviceAddress; memcpy(data, &instance, sizeof(VkAccelerationStructureInstanceKHR)); data += sizeof(VkAccelerationStructureInstanceKHR); } TopLevelAS.TransferBuffer->Unmap(); fb->GetCommands()->GetTransferCommands()->copyBuffer(TopLevelAS.TransferBuffer.get(), TopLevelAS.InstanceBuffer.get()); } void VkLevelMesh::RaytraceScene(const VkRenderPassKey& renderPassKey, VulkanCommandBuffer* commands, const FVector3& cameraPos, const VSMatrix& viewToWorld, float fovy, float aspect) { auto& pipeline = Viewer.Pipeline[renderPassKey]; if (!pipeline) { GraphicsPipelineBuilder builder; builder.RenderPass(fb->GetRenderPassManager()->GetRenderPass(renderPassKey)->GetRenderPass(0)); builder.Layout(Viewer.PipelineLayout.get()); builder.Topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP); builder.AddVertexShader(Viewer.VertexShader.get()); builder.AddFragmentShader(Viewer.FragmentShader.get()); builder.AddDynamicState(VK_DYNAMIC_STATE_VIEWPORT); builder.AddDynamicState(VK_DYNAMIC_STATE_SCISSOR); builder.RasterizationSamples((VkSampleCountFlagBits)renderPassKey.Samples); builder.DebugName("Viewer.Pipeline"); pipeline = builder.Create(fb->GetDevice()); } /* RenderPassBegin() .RenderPass(Viewer.RenderPass.get()) .Framebuffer(Framebuffers[imageIndex].get()) .RenderArea(0, 0, CurrentWidth, CurrentHeight) .AddClearColor(0.0f, 0.0f, 0.0f, 1.0f) .AddClearDepth(1.0f) .Execute(commands); VkViewport viewport = {}; viewport.width = (float)CurrentWidth; viewport.height = (float)CurrentHeight; viewport.maxDepth = 1.0f; commands->setViewport(0, 1, &viewport); VkRect2D scissor = {}; scissor.extent.width = CurrentWidth; scissor.extent.height = CurrentHeight; commands->setScissor(0, 1, &scissor); */ float f = 1.0f / std::tan(fovy * (pi::pif() / 360.0f)); ViewerPushConstants pushconstants; pushconstants.ViewToWorld = viewToWorld; pushconstants.CameraPos = cameraPos; pushconstants.ProjX = f / aspect; pushconstants.ProjY = f; pushconstants.SunDir = FVector3(Mesh->SunDirection.X, Mesh->SunDirection.Z, Mesh->SunDirection.Y); pushconstants.SunColor = Mesh->SunColor; pushconstants.SunIntensity = 1.0f; commands->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, Viewer.PipelineLayout.get(), 0, Viewer.DescriptorSet.get()); commands->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, Viewer.PipelineLayout.get(), 1, fb->GetDescriptorSetManager()->GetBindlessSet()); commands->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.get()); commands->pushConstants(Viewer.PipelineLayout.get(), VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(ViewerPushConstants), &pushconstants); commands->draw(4, 1, 0, 0); //commands->endRenderPass(); } void VkLevelMesh::CreateViewerObjects() { DescriptorSetLayoutBuilder builder; builder.AddBinding(0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT); builder.AddBinding(1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT); 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); if (useRayQuery) { builder.AddBinding(5, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1, VK_SHADER_STAGE_FRAGMENT_BIT); } else { builder.AddBinding(5, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT); } builder.AddBinding(6, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT); builder.AddBinding(7, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT); builder.DebugName("Viewer.DescriptorSetLayout"); Viewer.DescriptorSetLayout = builder.Create(fb->GetDevice()); DescriptorPoolBuilder poolbuilder; if (useRayQuery) { poolbuilder.AddPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 7); poolbuilder.AddPoolSize(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1); } else { poolbuilder.AddPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 8); } poolbuilder.MaxSets(1); poolbuilder.DebugName("Viewer.DescriptorPool"); Viewer.DescriptorPool = poolbuilder.Create(fb->GetDevice()); Viewer.DescriptorSet = Viewer.DescriptorPool->allocate(Viewer.DescriptorSetLayout.get()); Viewer.DescriptorSet->SetDebugName("raytrace.descriptorSet1"); std::string versionBlock = R"( #version 460 #extension GL_GOOGLE_include_directive : enable #extension GL_EXT_nonuniform_qualifier : enable )"; if (useRayQuery) { versionBlock += "#extension GL_EXT_ray_query : require\r\n"; versionBlock += "#define USE_RAYQUERY\r\n"; } auto onIncludeLocal = [](std::string headerName, std::string includerName, size_t depth) { return OnInclude(headerName.c_str(), includerName.c_str(), depth, false); }; auto onIncludeSystem = [](std::string headerName, std::string includerName, size_t depth) { return OnInclude(headerName.c_str(), includerName.c_str(), depth, true); }; Viewer.VertexShader = ShaderBuilder() .Type(ShaderType::Vertex) .AddSource("versionblock", versionBlock) .AddSource("vert_viewer.glsl", LoadPrivateShaderLump("shaders/lightmap/vert_viewer.glsl").GetChars()) .OnIncludeLocal(onIncludeLocal) .OnIncludeSystem(onIncludeSystem) .DebugName("Viewer.VertexShader") .Create("vertex", fb->GetDevice()); Viewer.FragmentShader = ShaderBuilder() .Type(ShaderType::Fragment) .AddSource("versionblock", versionBlock) .AddSource("frag_viewer.glsl", LoadPrivateShaderLump("shaders/lightmap/frag_viewer.glsl").GetChars()) .OnIncludeLocal(onIncludeLocal) .OnIncludeSystem(onIncludeSystem) .DebugName("Viewer.FragmentShader") .Create("vertex", fb->GetDevice()); Viewer.PipelineLayout = PipelineLayoutBuilder() .AddSetLayout(Viewer.DescriptorSetLayout.get()) .AddSetLayout(fb->GetDescriptorSetManager()->GetBindlessLayout()) .AddPushConstantRange(VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(ViewerPushConstants)) .DebugName("Viewer.PipelineLayout") .Create(fb->GetDevice()); /* Viewer.RenderPass = RenderPassBuilder() .AddAttachment(VK_FORMAT_B8G8R8A8_UNORM, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_STORE, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) .AddSubpass() .AddSubpassColorAttachmentRef(0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) .Create(fb->GetDevice()); Viewer.Pipeline = GraphicsPipelineBuilder() .RenderPass(Viewer.RenderPass.get()) .Layout(Viewer.PipelineLayout.get()) .Topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP) .AddVertexShader(Viewer.VertexShader.get()) .AddFragmentShader(Viewer.FragmentShader.get()) .AddDynamicState(VK_DYNAMIC_STATE_VIEWPORT) .AddDynamicState(VK_DYNAMIC_STATE_SCISSOR) .DebugName("Viewer.Pipeline") .Create(fb->GetDevice()); */ } FString VkLevelMesh::LoadPrivateShaderLump(const char* lumpname) { int lump = fileSystem.CheckNumForFullName(lumpname, 0); if (lump == -1) I_Error("Unable to load '%s'", lumpname); return GetStringFromLump(lump); } FString VkLevelMesh::LoadPublicShaderLump(const char* lumpname) { int lump = fileSystem.CheckNumForFullName(lumpname, 0); if (lump == -1) lump = fileSystem.CheckNumForFullName(lumpname); if (lump == -1) I_Error("Unable to load '%s'", lumpname); return GetStringFromLump(lump); } ShaderIncludeResult VkLevelMesh::OnInclude(FString headerName, FString includerName, size_t depth, bool system) { if (depth > 8) I_Error("Too much include recursion!"); FString includeguardname; includeguardname << "_HEADERGUARD_" << headerName.GetChars(); includeguardname.ReplaceChars("/\\.", '_'); FString code; code << "#ifndef " << includeguardname.GetChars() << "\n"; code << "#define " << includeguardname.GetChars() << "\n"; code << "#line 1\n"; if (system) code << LoadPrivateShaderLump(headerName.GetChars()).GetChars() << "\n"; else code << LoadPublicShaderLump(headerName.GetChars()).GetChars() << "\n"; code << "#endif\n"; return ShaderIncludeResult(headerName.GetChars(), code.GetChars()); } ///////////////////////////////////////////////////////////////////////////// VkLevelMeshUploader::VkLevelMeshUploader(VkLevelMesh* mesh) : Mesh(mesh) { } void VkLevelMeshUploader::Upload() { size_t transferBufferSize = GetTransferSize(); if (transferBufferSize == 0) { ClearRanges(); return; } BeginTransfer(transferBufferSize); UploadNodes(); UploadRanges(Mesh->Mesh->UploadRanges.Vertex, Mesh->Mesh->Mesh.Vertices.Data(), Mesh->VertexBuffer.get()); UploadRanges(Mesh->Mesh->UploadRanges.UniformIndexes, Mesh->Mesh->Mesh.UniformIndexes.Data(), Mesh->UniformIndexBuffer.get()); UploadRanges(Mesh->Mesh->UploadRanges.Index, Mesh->Mesh->Mesh.Indexes.Data(), Mesh->IndexBuffer.get()); UploadRanges(Mesh->Mesh->UploadRanges.SurfaceIndex, Mesh->Mesh->Mesh.SurfaceIndexes.Data(), Mesh->SurfaceIndexBuffer.get()); UploadRanges(Mesh->Mesh->UploadRanges.LightIndex, Mesh->Mesh->Mesh.LightIndexes.Data(), Mesh->LightIndexBuffer.get()); UploadRanges(Mesh->Mesh->UploadRanges.DrawIndex, Mesh->Mesh->Mesh.DrawIndexes.Data(), Mesh->DrawIndexBuffer.get()); UploadSurfaces(); UploadUniforms(); UploadPortals(); UploadLights(); EndTransfer(transferBufferSize); // We can't add these as we go because UploadUniforms might load textures, which may invalidate the transfer command buffer. VulkanCommandBuffer* cmdbuffer = Mesh->fb->GetCommands()->GetTransferCommands(); for (const CopyCommand& copy : copyCommands) { cmdbuffer->copyBuffer(copy.srcBuffer, copy.dstBuffer, copy.srcOffset, copy.dstOffset, copy.size); } copyCommands.clear(); Mesh->fb->GetCommands()->TransferDeleteList->Add(std::move(transferBuffer)); ClearRanges(); } void VkLevelMeshUploader::ClearRanges() { Mesh->Mesh->UploadRanges.Vertex.clear(); Mesh->Mesh->UploadRanges.Index.clear(); Mesh->Mesh->UploadRanges.Node.clear(); Mesh->Mesh->UploadRanges.SurfaceIndex.clear(); Mesh->Mesh->UploadRanges.Surface.clear(); Mesh->Mesh->UploadRanges.UniformIndexes.clear(); Mesh->Mesh->UploadRanges.Uniforms.clear(); Mesh->Mesh->UploadRanges.Portals.clear(); Mesh->Mesh->UploadRanges.Light.clear(); Mesh->Mesh->UploadRanges.LightIndex.clear(); Mesh->Mesh->UploadRanges.DrawIndex.clear(); } void VkLevelMeshUploader::BeginTransfer(size_t transferBufferSize) { transferBuffer = BufferBuilder() .Usage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY) .Size(transferBufferSize) .DebugName("UploadMeshes") .Create(Mesh->fb->GetDevice()); data = (uint8_t*)transferBuffer->Map(0, transferBufferSize); datapos = 0; } void VkLevelMeshUploader::EndTransfer(size_t transferBufferSize) { assert(datapos == transferBufferSize); transferBuffer->Unmap(); } static FVector3 SwapYZ(const FVector3& v) { return FVector3(v.X, v.Z, v.Y); } void VkLevelMeshUploader::UploadNodes() { if (Mesh->useRayQuery) return; // Always update the header struct of the collision storage buffer block if something changed if (Mesh->Mesh->UploadRanges.Node.Size() > 0) { CollisionNodeBufferHeader nodesHeader; nodesHeader.root = Mesh->Mesh->Collision->get_root(); *((CollisionNodeBufferHeader*)(data + datapos)) = nodesHeader; copyCommands.emplace_back(transferBuffer.get(), Mesh->NodeBuffer.get(), datapos, 0, sizeof(CollisionNodeBufferHeader)); datapos += sizeof(CollisionNodeBufferHeader) + sizeof(CollisionNode); } // Copy collision nodes for (const MeshBufferRange& range : Mesh->Mesh->UploadRanges.Node) { const auto& srcnodes = Mesh->Mesh->Collision->get_nodes(); CollisionNode* nodes = (CollisionNode*)(data + datapos); for (int i = 0, count = range.Count(); i < count; i++) { const auto& node = srcnodes[range.Start + i]; CollisionNode info; info.center = SwapYZ(node.aabb.Center); info.extents = SwapYZ(node.aabb.Extents); info.left = node.left; info.right = node.right; info.element_index = node.element_index; *(nodes++) = info; } size_t copysize = range.Count() * sizeof(CollisionNode); if (copysize > 0) copyCommands.emplace_back(transferBuffer.get(), Mesh->NodeBuffer.get(), datapos, sizeof(CollisionNodeBufferHeader) + range.Start * sizeof(CollisionNode), copysize); datapos += copysize; } } template void VkLevelMeshUploader::UploadRanges(const TArray& ranges, const T* srcbuffer, VulkanBuffer* destbuffer) { for (const MeshBufferRange& range : ranges) { size_t copysize = range.Count() * sizeof(T); memcpy(data + datapos, srcbuffer + range.Start, copysize); if (copysize > 0) copyCommands.emplace_back(transferBuffer.get(), destbuffer, datapos, range.Start * sizeof(T), copysize); datapos += copysize; } } void VkLevelMeshUploader::UploadSurfaces() { for (const MeshBufferRange& range : Mesh->Mesh->UploadRanges.Surface) { SurfaceInfo* surfaces = (SurfaceInfo*)(data + datapos); for (int j = 0, count = range.Count(); j < count; j++) { LevelMeshSurface* surface = &Mesh->Mesh->Mesh.Surfaces[range.Start + j]; SurfaceInfo info; info.Normal = FVector3(surface->Plane.X, surface->Plane.Z, surface->Plane.Y); info.PortalIndex = surface->PortalIndex; info.Sky = surface->IsSky; info.Alpha = surface->Alpha; if (surface->Texture) { auto mat = FMaterial::ValidateTexture(surface->Texture, 0); info.TextureIndex = Mesh->fb->GetBindlessTextureIndex(mat, CLAMP_NONE, 0); } else { info.TextureIndex = 0; } info.LightStart = surface->LightList.Pos; info.LightEnd = surface->LightList.Pos + surface->LightList.Count; *(surfaces++) = info; } size_t copysize = range.Count() * sizeof(SurfaceInfo); if (copysize > 0) copyCommands.emplace_back(transferBuffer.get(), Mesh->SurfaceBuffer.get(), datapos, range.Start * sizeof(SurfaceInfo), copysize); datapos += copysize; } } void VkLevelMeshUploader::UploadUniforms() { for (const MeshBufferRange& range : Mesh->Mesh->UploadRanges.Uniforms) { for (int j = 0, count = range.Count(); j < count; j++) { auto& surfaceUniforms = Mesh->Mesh->Mesh.Uniforms[range.Start + j]; auto& material = Mesh->Mesh->Mesh.Materials[range.Start + j]; if (material.mMaterial) { auto source = material.mMaterial->Source(); surfaceUniforms.uSpecularMaterial = { source->GetGlossiness(), source->GetSpecularLevel() }; surfaceUniforms.uDepthFadeThreshold = source->GetDepthFadeThreshold(); surfaceUniforms.uTextureIndex = Mesh->fb->GetBindlessTextureIndex(material.mMaterial, material.mClampMode, material.mTranslation); } else { surfaceUniforms.uDepthFadeThreshold = 0.f; surfaceUniforms.uTextureIndex = 0; } } SurfaceUniforms* uniforms = (SurfaceUniforms*)(data + datapos); size_t copysize = range.Count() * sizeof(SurfaceUniforms); memcpy(uniforms, Mesh->Mesh->Mesh.Uniforms.Data() + range.Start, copysize); if (copysize > 0) copyCommands.emplace_back(transferBuffer.get(), Mesh->UniformsBuffer.get(), datapos, range.Start * sizeof(SurfaceUniforms), copysize); datapos += copysize; } } void VkLevelMeshUploader::UploadPortals() { for (const MeshBufferRange& range : Mesh->Mesh->UploadRanges.Portals) { PortalInfo* portals = (PortalInfo*)(data + datapos); for (int i = 0, count = range.Count(); i < count; i++) { const auto& portal = Mesh->Mesh->Portals[range.Start + i]; PortalInfo info; info.transformation = portal.transformation; *(portals++) = info; } size_t copysize = range.Count() * sizeof(PortalInfo); if (copysize > 0) copyCommands.emplace_back(transferBuffer.get(), Mesh->PortalBuffer.get(), datapos, range.Start * sizeof(PortalInfo), copysize); datapos += copysize; } } void VkLevelMeshUploader::UploadLights() { for (const MeshBufferRange& range : Mesh->Mesh->UploadRanges.Light) { LightInfo* lights = (LightInfo*)(data + datapos); for (int i = 0, count = range.Count(); i < count; i++) { const auto& light = Mesh->Mesh->Mesh.Lights[range.Start + i]; LightInfo info; info.Origin = SwapYZ(light.Origin); info.RelativeOrigin = SwapYZ(light.RelativeOrigin); info.Radius = light.Radius; info.Intensity = light.Intensity; info.InnerAngleCos = light.InnerAngleCos; info.OuterAngleCos = light.OuterAngleCos; info.SpotDir = SwapYZ(light.SpotDir); info.Color = light.Color; info.SoftShadowRadius = light.SoftShadowRadius; *(lights++) = info; } size_t copysize = range.Count() * sizeof(LightInfo); if (copysize > 0) copyCommands.emplace_back(transferBuffer.get(), Mesh->LightBuffer.get(), datapos, range.Start * sizeof(LightInfo), copysize); datapos += copysize; } } size_t VkLevelMeshUploader::GetTransferSize() { // Figure out how much memory we need to transfer it to the GPU size_t transferBufferSize = 0; if (!Mesh->useRayQuery) { if (Mesh->Mesh->UploadRanges.Node.Size() > 0) transferBufferSize += sizeof(CollisionNodeBufferHeader) + sizeof(CollisionNode); for (const MeshBufferRange& range : Mesh->Mesh->UploadRanges.Node) transferBufferSize += range.Count() * sizeof(CollisionNode); } for (const MeshBufferRange& range : Mesh->Mesh->UploadRanges.Vertex) transferBufferSize += range.Count() * sizeof(FFlatVertex); for (const MeshBufferRange& range : Mesh->Mesh->UploadRanges.UniformIndexes) transferBufferSize += range.Count() * sizeof(int); for (const MeshBufferRange& range : Mesh->Mesh->UploadRanges.Index) transferBufferSize += range.Count() * sizeof(uint32_t); for (const MeshBufferRange& range : Mesh->Mesh->UploadRanges.SurfaceIndex) transferBufferSize += range.Count() * sizeof(int); for (const MeshBufferRange& range : Mesh->Mesh->UploadRanges.Surface) transferBufferSize += range.Count() * sizeof(SurfaceInfo); for (const MeshBufferRange& range : Mesh->Mesh->UploadRanges.Uniforms) transferBufferSize += range.Count() * sizeof(SurfaceUniforms); for (const MeshBufferRange& range : Mesh->Mesh->UploadRanges.Portals) transferBufferSize += range.Count() * sizeof(PortalInfo); for (const MeshBufferRange& range : Mesh->Mesh->UploadRanges.LightIndex) transferBufferSize += range.Count() * sizeof(int32_t); for (const MeshBufferRange& range : Mesh->Mesh->UploadRanges.Light) transferBufferSize += range.Count() * sizeof(LightInfo); for (const MeshBufferRange& range : Mesh->Mesh->UploadRanges.DrawIndex) transferBufferSize += range.Count() * sizeof(uint32_t); return transferBufferSize; }