From c53ebe81219ae66b18f961c77d520e6ea57bae94 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Wed, 17 Aug 2022 16:10:10 +0200 Subject: [PATCH] Only process lights within range of a surface --- src/lightmap/collision.cpp | 26 ++++++++ src/lightmap/collision.h | 4 ++ src/lightmap/gpuraytracer2.cpp | 107 +++++++++++++++++++-------------- src/lightmap/gpuraytracer2.h | 8 ++- src/lightmap/levelmesh.cpp | 29 +++++++++ src/lightmap/levelmesh.h | 7 +++ 6 files changed, 135 insertions(+), 46 deletions(-) diff --git a/src/lightmap/collision.cpp b/src/lightmap/collision.cpp index c08a40e..fd87e59 100644 --- a/src/lightmap/collision.cpp +++ b/src/lightmap/collision.cpp @@ -68,6 +68,13 @@ bool TriangleMeshShape::find_any_hit(TriangleMeshShape *shape1, SphereShape *sha return find_any_hit(shape1, shape2, shape1->root); } +std::vector TriangleMeshShape::find_all_hits(TriangleMeshShape* shape1, SphereShape* shape2) +{ + std::vector hits; + find_all_hits(shape1, shape2, shape1->root, hits); + return hits; +} + bool TriangleMeshShape::find_any_hit(TriangleMeshShape *shape, const vec3 &ray_start, const vec3 &ray_end) { return find_any_hit(shape, RayBBox(ray_start, ray_end), shape->root); @@ -133,6 +140,25 @@ bool TriangleMeshShape::find_any_hit(TriangleMeshShape *shape1, SphereShape *sha return false; } +void TriangleMeshShape::find_all_hits(TriangleMeshShape* shape1, SphereShape* shape2, int a, std::vector& hits) +{ + if (overlap_bv_sphere(shape1, shape2, a)) + { + if (shape1->is_leaf(a)) + { + if (overlap_triangle_sphere(shape1, shape2, a)) + { + hits.push_back(shape1->nodes[a].element_index / 3); + } + } + else + { + find_all_hits(shape1, shape2, shape1->nodes[a].left, hits); + find_all_hits(shape1, shape2, shape1->nodes[a].right, hits); + } + } +} + bool TriangleMeshShape::find_any_hit(TriangleMeshShape *shape1, TriangleMeshShape *shape2, int a, int b) { bool leaf_a = shape1->is_leaf(a); diff --git a/src/lightmap/collision.h b/src/lightmap/collision.h index 290d9f2..8569d98 100644 --- a/src/lightmap/collision.h +++ b/src/lightmap/collision.h @@ -97,6 +97,8 @@ public: static bool find_any_hit(TriangleMeshShape *shape1, SphereShape *shape2); static bool find_any_hit(TriangleMeshShape *shape, const vec3 &ray_start, const vec3 &ray_end); + static std::vector find_all_hits(TriangleMeshShape* shape1, SphereShape* shape2); + static TraceHit find_first_hit(TriangleMeshShape *shape, const vec3 &ray_start, const vec3 &ray_end); private: @@ -126,6 +128,8 @@ private: static bool find_any_hit(TriangleMeshShape *shape1, SphereShape *shape2, int a); static bool find_any_hit(TriangleMeshShape *shape1, const RayBBox &ray, int a); + static void find_all_hits(TriangleMeshShape* shape1, SphereShape* shape2, int a, std::vector& hits); + static void find_first_hit(TriangleMeshShape *shape1, const RayBBox &ray, int a, TraceHit *hit); inline static bool overlap_bv_ray(TriangleMeshShape *shape, const RayBBox &ray, int a); diff --git a/src/lightmap/gpuraytracer2.cpp b/src/lightmap/gpuraytracer2.cpp index 968f019..dfdd1b5 100644 --- a/src/lightmap/gpuraytracer2.cpp +++ b/src/lightmap/gpuraytracer2.cpp @@ -101,16 +101,6 @@ void GPURaytracer2::Raytrace(LevelMesh* level) int sampleWidth = surface->texWidth; int sampleHeight = surface->texHeight; - PushConstants2 pc; - pc.LightStart = 0; - pc.LightEnd = mesh->map->ThingLights.Size(); - pc.SurfaceIndex = (int32_t)i; - pc.TileTL = vec2(0.0f); - pc.TileBR = vec2(1.0f); - pc.LightmapOrigin = surface->worldOrigin; - pc.LightmapStepX = surface->worldStepX * (float)sampleWidth; - pc.LightmapStepY = surface->worldStepY * (float)sampleHeight; - #if 1 int firstVertex = sceneVertexPos; int vertexCount = 4; @@ -133,15 +123,42 @@ void GPURaytracer2::Raytrace(LevelMesh* level) } #endif + int firstLight = sceneLightPos; + int lightCount = (int)surface->LightList.size(); + sceneLightPos += lightCount; + + LightInfo2* lightinfo = &sceneLights[firstLight]; + for (ThingLight* light : surface->LightList) + { + lightinfo->Origin = light->LightOrigin(); + lightinfo->Radius = light->LightRadius(); + lightinfo->Intensity = light->intensity; + lightinfo->InnerAngleCos = light->innerAngleCos; + lightinfo->OuterAngleCos = light->outerAngleCos; + lightinfo->SpotDir = light->SpotDir(); + lightinfo->Color = light->rgb; + lightinfo++; + } + VkViewport viewport = {}; viewport.maxDepth = 1; viewport.x = (float)surface->atlasX; viewport.y = (float)surface->atlasY; viewport.width = (float)sampleWidth; viewport.height = (float)sampleHeight; - cmdbuffer->setViewport(0, 1, &viewport); + + PushConstants2 pc; + pc.LightStart = firstLight; + pc.LightEnd = firstLight + lightCount; + pc.SurfaceIndex = (int32_t)i; + pc.TileTL = vec2(0.0f); + pc.TileBR = vec2(1.0f); + pc.LightmapOrigin = surface->worldOrigin; + pc.LightmapStepX = surface->worldStepX * (float)sampleWidth; + pc.LightmapStepY = surface->worldStepY * (float)sampleHeight; cmdbuffer->pushConstants(pipelineLayout.get(), VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushConstants2), &pc); + cmdbuffer->draw(vertexCount, 1, firstVertex, 0); } cmdbuffer->endRenderPass(); @@ -164,8 +181,20 @@ void GPURaytracer2::Raytrace(LevelMesh* level) cmdbuffer->copyImageToBuffer(img.Image->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, img.Transfer->buffer, 1, ®ion); } +#ifdef WIN32 + LARGE_INTEGER s; + QueryPerformanceCounter(&s); +#endif + FinishCommands(); +#ifdef WIN32 + LARGE_INTEGER e, f; + QueryPerformanceCounter(&e); + QueryPerformanceFrequency(&f); + printf("GPU ray tracing time was %.3f seconds.\n", double(e.QuadPart - s.QuadPart) / double(f.QuadPart)); +#endif + for (size_t pageIndex = 0; pageIndex < atlasImages.size(); pageIndex++) { vec4* pixels = (vec4*)atlasImages[pageIndex].Transfer->Map(0, atlasImageSize * atlasImageSize * sizeof(vec4)); @@ -207,6 +236,7 @@ void GPURaytracer2::CreateVulkanObjects() BeginCommands(); CreateSceneVertexBuffer(); + CreateSceneLightBuffer(); CreateVertexAndIndexBuffers(); CreateBottomLevelAccelerationStructure(); CreateTopLevelAccelerationStructure(); @@ -236,6 +266,25 @@ void GPURaytracer2::CreateSceneVertexBuffer() sceneVertexPos = 0; } +void GPURaytracer2::CreateSceneLightBuffer() +{ + size_t size = sizeof(LightInfo2) * SceneLightBufferSize; + + sceneLightBuffer = BufferBuilder() + .Usage( + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, + VMA_MEMORY_USAGE_UNKNOWN, VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT) + .MemoryType( + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) + .Size(size) + .DebugName("SceneLightBuffer") + .Create(device.get()); + + sceneLights = (LightInfo2*)sceneLightBuffer->Map(0, size); + sceneLightPos = 0; +} + void GPURaytracer2::BeginCommands() { cmdbuffer = cmdpool->createBuffer(); @@ -262,19 +311,15 @@ void GPURaytracer2::FinishCommands() void GPURaytracer2::CreateVertexAndIndexBuffers() { std::vector surfaces = CreateSurfaceInfo(); - std::vector lights = CreateLightInfo(); - if (lights.empty()) // vulkan doesn't support zero byte buffers - lights.push_back(LightInfo2()); - if (surfaces.empty()) + if (surfaces.empty()) // vulkan doesn't support zero byte buffers surfaces.push_back(SurfaceInfo2()); 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 transferbuffersize = vertexbuffersize + indexbuffersize + surfaceindexbuffersize + surfacebuffersize; size_t vertexoffset = 0; size_t indexoffset = vertexoffset + vertexbuffersize; size_t surfaceindexoffset = indexoffset + indexbuffersize; @@ -315,12 +360,6 @@ void GPURaytracer2::CreateVertexAndIndexBuffers() .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) @@ -332,14 +371,12 @@ void GPURaytracer2::CreateVertexAndIndexBuffers() 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); PipelineBarrier() .AddMemory(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT) @@ -613,7 +650,7 @@ void GPURaytracer2::CreateDescriptorSet() .AddBuffer(descriptorSet.get(), 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 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()) + .AddBuffer(descriptorSet.get(), 4, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, sceneLightBuffer.get()) .Execute(device.get()); } @@ -672,24 +709,6 @@ std::vector GPURaytracer2::CreateSurfaceInfo() return surfaces; } -std::vector GPURaytracer2::CreateLightInfo() -{ - std::vector lights; - for (ThingLight& light : mesh->map->ThingLights) - { - LightInfo2 info; - info.Origin = light.LightOrigin(); - info.Radius = light.LightRadius(); - info.Intensity = light.intensity; - info.InnerAngleCos = light.innerAngleCos; - info.OuterAngleCos = light.outerAngleCos; - info.SpotDir = light.SpotDir(); - info.Color = light.rgb; - lights.push_back(info); - } - return lights; -} - void GPURaytracer2::PrintVulkanInfo() { const auto& props = device->physicalDevice.properties; diff --git a/src/lightmap/gpuraytracer2.h b/src/lightmap/gpuraytracer2.h index be25cb0..0aa2182 100644 --- a/src/lightmap/gpuraytracer2.h +++ b/src/lightmap/gpuraytracer2.h @@ -85,6 +85,7 @@ private: void CreatePipeline(); void CreateDescriptorSet(); void CreateSceneVertexBuffer(); + void CreateSceneLightBuffer(); LightmapImage CreateImage(int width, int height); @@ -94,7 +95,6 @@ private: void PrintVulkanInfo(); std::vector CreateSurfaceInfo(); - std::vector CreateLightInfo(); LevelMesh* mesh = nullptr; @@ -110,12 +110,16 @@ private: SceneVertex* sceneVertices = nullptr; int sceneVertexPos = 0; + static const int SceneLightBufferSize = 1 * 1024 * 1024; + std::unique_ptr sceneLightBuffer; + LightInfo2* sceneLights = nullptr; + int sceneLightPos = 0; + std::unique_ptr vertexBuffer; std::unique_ptr indexBuffer; std::unique_ptr transferBuffer; std::unique_ptr surfaceIndexBuffer; std::unique_ptr surfaceBuffer; - std::unique_ptr lightBuffer; std::unique_ptr blScratchBuffer; std::unique_ptr blAccelStructBuffer; diff --git a/src/lightmap/levelmesh.cpp b/src/lightmap/levelmesh.cpp index 1ac8d1f..8c95cea 100644 --- a/src/lightmap/levelmesh.cpp +++ b/src/lightmap/levelmesh.cpp @@ -112,6 +112,35 @@ LevelMesh::LevelMesh(FLevel &doomMap, int sampleDistance, int textureSize) { BuildSurfaceParams(surfaces[i].get()); } + + printf("Building collision data...\n\n"); + + Collision = std::make_unique(MeshVertices.Data(), MeshVertices.Size(), MeshElements.Data(), MeshElements.Size()); + + printf("Building light list...\n\n"); + + for (ThingLight& light : map->ThingLights) + { + SphereShape sphere; + sphere.center = light.LightOrigin(); + sphere.radius = light.LightRadius(); + + for (int triangleIndex : TriangleMeshShape::find_all_hits(Collision.get(), &sphere)) + { + Surface* surface = surfaces[MeshSurfaces[triangleIndex]].get(); + bool found = false; + for (ThingLight* light2 : surface->LightList) + { + if (light2 == &light) + { + found = true; + break; + } + } + if (!found) + surface->LightList.push_back(&light); + } + } } // Determines a lightmap block in which to map to the lightmap texture. diff --git a/src/lightmap/levelmesh.h b/src/lightmap/levelmesh.h index 365d1d4..17833ac 100644 --- a/src/lightmap/levelmesh.h +++ b/src/lightmap/levelmesh.h @@ -36,6 +36,7 @@ #include "framework/halffloat.h" #include "lightmaptexture.h" #include "math/mathlib.h" +#include "collision.h" #include "dp_rect_pack/dp_rect_pack.h" @@ -45,6 +46,7 @@ struct MapSubsectorEx; struct IntSector; struct IntSideDef; struct FLevel; +struct ThingLight; class FWadWriter; enum SurfaceType @@ -74,6 +76,9 @@ struct Surface int sampleDimension = 0; bool bSky = false; + // Touching light sources + std::vector LightList; + // Lightmap world coordinates for the texture vec3 worldOrigin = { 0.0f }; vec3 worldStepX = { 0.0f }; @@ -125,6 +130,8 @@ public: TArray MeshElements; TArray MeshSurfaces; + std::unique_ptr Collision; + private: void CreateSubsectorSurfaces(FLevel &doomMap); void CreateCeilingSurface(FLevel &doomMap, MapSubsectorEx *sub, IntSector *sector, int typeIndex, bool is3DFloor);