Speed up rayquery raytracer by using an atlas

This commit is contained in:
Magnus Norddahl 2022-08-17 01:35:12 +02:00
parent 89a3927a66
commit 0ca74c2e42
4 changed files with 135 additions and 57 deletions

View file

@ -20,7 +20,7 @@
#include "level/level.h"
#include "lightmap/cpuraytracer.h"
#include "lightmap/gpuraytracer2.h"
#include "lightmap/gpuraytracer.h"
#include "math/vec.h"
//#include "rejectbuilder.h"
#include <memory>
@ -767,12 +767,12 @@ void FProcessor::BuildLightmaps()
LightmapMesh = std::make_unique<LevelMesh>(Level, Level.DefaultSamples, LMDims);
std::unique_ptr<GPURaytracer2> gpuraytracer;
std::unique_ptr<GPURaytracer> gpuraytracer;
if (!CPURaytrace)
{
try
{
gpuraytracer = std::make_unique<GPURaytracer2>();
gpuraytracer = std::make_unique<GPURaytracer>();
}
catch (std::exception msg)
{

View file

@ -18,20 +18,13 @@ layout(push_constant) uniform PushConstants
float PushPadding4;
};
layout(location = 0) in vec2 aPosition;
layout(location = 0) out vec3 worldpos;
vec2 positions[4] = vec2[](
vec2(0.0, 0.0),
vec2(1.0, 0.0),
vec2(0.0, 1.0),
vec2(1.0, 1.0)
);
void main()
{
vec2 tilepos = positions[gl_VertexIndex];
worldpos = LightmapOrigin + LightmapStepX * tilepos.x + LightmapStepY * tilepos.y;
gl_Position = vec4(mix(TileTL, TileBR, tilepos) * 2.0 - 1.0, 0.0, 1.0);
worldpos = LightmapOrigin + LightmapStepX * aPosition.x + LightmapStepY * aPosition.y;
gl_Position = vec4(aPosition * 2.0 - 1.0, 0.0, 1.0);
}
)glsl";

View file

@ -58,59 +58,98 @@ void GPURaytracer2::Raytrace(LevelMesh* level)
.AddBuffer(uniformBuffer.get(), VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT)
.Execute(cmdbuffer.get(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
std::vector<LightmapImage> surfaceImages;
const int atlasImageSize = 512;
RectPacker packer(atlasImageSize, atlasImageSize, RectPacker::Spacing(0));
for (size_t i = 0; i < mesh->surfaces.size(); i++)
{
Surface* surface = mesh->surfaces[i].get();
int sampleWidth = surface->texWidth;
int sampleHeight = surface->texHeight;
surfaceImages.push_back(CreateImage(sampleWidth, sampleHeight));
auto result = packer.insert(surface->texWidth, surface->texHeight);
surface->atlasX = result.pos.x;
surface->atlasY = result.pos.y;
surface->atlasPageIndex = (int)result.pageIndex;
}
for (size_t i = 0; i < mesh->surfaces.size(); i++)
std::vector<LightmapImage> atlasImages;
for (size_t pageIndex = 0; pageIndex < packer.getNumPages(); pageIndex++)
{
Surface* surface = mesh->surfaces[i].get();
int sampleWidth = surface->texWidth;
int sampleHeight = surface->texHeight;
LightmapImage& img = surfaceImages[i];
atlasImages.push_back(CreateImage(atlasImageSize, atlasImageSize));
}
for (size_t pageIndex = 0; pageIndex < atlasImages.size(); pageIndex++)
{
LightmapImage& img = atlasImages[pageIndex];
RenderPassBegin()
.RenderPass(renderPass.get())
.RenderArea(0, 0, sampleWidth, sampleHeight)
.RenderArea(0, 0, atlasImageSize, atlasImageSize)
.Framebuffer(img.Framebuffer.get())
.Execute(cmdbuffer.get());
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;
VkViewport viewport = {};
viewport.maxDepth = 1;
viewport.width = (float)sampleWidth;
viewport.height = (float)sampleHeight;
cmdbuffer->setViewport(0, 1, &viewport);
VkDeviceSize offset = 0;
cmdbuffer->bindVertexBuffers(0, 1, &sceneVertexBuffer->buffer, &offset);
cmdbuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.get());
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout.get(), 0, descriptorSet.get());
cmdbuffer->pushConstants(pipelineLayout.get(), VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushConstants2), &pc);
cmdbuffer->draw(4, 1, 0, 0);
for (size_t i = 0; i < mesh->surfaces.size(); i++)
{
Surface* surface = mesh->surfaces[i].get();
if (surface->atlasPageIndex != pageIndex)
continue;
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;
sceneVertexPos += vertexCount;
SceneVertex* vertex = &sceneVertices[firstVertex];
vertex[0].Position = vec2(0.0f, 0.0f);
vertex[1].Position = vec2(1.0f, 0.0f);
vertex[2].Position = vec2(1.0f, 1.0f);
vertex[3].Position = vec2(0.0f, 1.0f);
#else
int firstVertex = sceneVertexPos;
int vertexCount = (int)surface->lightUV.size();
sceneVertexPos += vertexCount;
SceneVertex* vertex = &sceneVertices[firstVertex];
for (const vec2& uv : surface->lightUV)
{
(vertex++)->Position = vec2(uv.x / sampleWidth, uv.y / sampleHeight);
}
#endif
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);
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();
}
for (size_t i = 0; i < mesh->surfaces.size(); i++)
for (size_t i = 0; i < atlasImages.size(); i++)
{
Surface* surface = mesh->surfaces[i].get();
int sampleWidth = surface->texWidth;
int sampleHeight = surface->texHeight;
LightmapImage& img = surfaceImages[i];
LightmapImage& img = atlasImages[i];
PipelineBarrier()
.AddImage(img.Image.get(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT)
@ -119,26 +158,39 @@ void GPURaytracer2::Raytrace(LevelMesh* level)
VkBufferImageCopy region = {};
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.layerCount = 1;
region.imageExtent.width = sampleWidth;
region.imageExtent.height = sampleHeight;
region.imageExtent.width = atlasImageSize;
region.imageExtent.height = atlasImageSize;
region.imageExtent.depth = 1;
cmdbuffer->copyImageToBuffer(img.Image->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, img.Transfer->buffer, 1, &region);
}
FinishCommands();
for (size_t i = 0; i < mesh->surfaces.size(); i++)
for (size_t pageIndex = 0; pageIndex < atlasImages.size(); pageIndex++)
{
Surface* surface = mesh->surfaces[i].get();
int sampleWidth = surface->texWidth;
int sampleHeight = surface->texHeight;
vec4* pixels = (vec4*)atlasImages[pageIndex].Transfer->Map(0, atlasImageSize * atlasImageSize * sizeof(vec4));
vec4* pixels = (vec4*)surfaceImages[i].Transfer->Map(0, sampleWidth * sampleHeight * sizeof(vec4));
for (int i = 0; i < sampleWidth * sampleHeight; i++)
for (size_t i = 0; i < mesh->surfaces.size(); i++)
{
surface->texPixels[i] = pixels[i].xyz();
Surface* surface = mesh->surfaces[i].get();
if (surface->atlasPageIndex != pageIndex)
continue;
int atlasX = surface->atlasX;
int atlasY = surface->atlasY;
int sampleWidth = surface->texWidth;
int sampleHeight = surface->texHeight;
for (int y = 0; y < sampleHeight; y++)
{
vec3* dest = &surface->texPixels[y * sampleWidth];
vec4* src = &pixels[atlasX + (atlasY + y) * atlasImageSize];
for (int x = 0; x < sampleWidth; x++)
{
dest[x] = src[x].xyz();
}
}
}
surfaceImages[i].Transfer->Unmap();
atlasImages[pageIndex].Transfer->Unmap();
}
if (device->renderdoc)
@ -154,6 +206,7 @@ void GPURaytracer2::CreateVulkanObjects()
BeginCommands();
CreateSceneVertexBuffer();
CreateVertexAndIndexBuffers();
CreateBottomLevelAccelerationStructure();
CreateTopLevelAccelerationStructure();
@ -164,6 +217,25 @@ void GPURaytracer2::CreateVulkanObjects()
FinishCommands();
}
void GPURaytracer2::CreateSceneVertexBuffer()
{
size_t size = sizeof(SceneVertex) * SceneVertexBufferSize;
sceneVertexBuffer = BufferBuilder()
.Usage(
VK_BUFFER_USAGE_VERTEX_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("SceneVertexBuffer")
.Create(device.get());
sceneVertices = (SceneVertex*)sceneVertexBuffer->Map(0, size);
sceneVertexPos = 0;
}
void GPURaytracer2::BeginCommands()
{
cmdbuffer = cmdpool->createBuffer();
@ -466,7 +538,9 @@ void GPURaytracer2::CreatePipeline()
.RenderPass(renderPass.get())
.AddVertexShader(vertShader.get())
.AddFragmentShader(fragShader.get())
.Topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP)
.AddVertexBufferBinding(0, sizeof(SceneVertex))
.AddVertexAttribute(0, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(SceneVertex, Position))
.Topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN)
.AddDynamicState(VK_DYNAMIC_STATE_VIEWPORT)
.Viewport(0.0f, 0.0f, 0.0f, 0.0f)
.Scissor(0, 0, 4096, 4096)

View file

@ -63,6 +63,11 @@ struct LightmapImage
std::unique_ptr<VulkanBuffer> Transfer;
};
struct SceneVertex
{
vec2 Position;
};
class GPURaytracer2
{
public:
@ -79,6 +84,7 @@ private:
void CreateShaders();
void CreatePipeline();
void CreateDescriptorSet();
void CreateSceneVertexBuffer();
LightmapImage CreateImage(int width, int height);
@ -99,6 +105,11 @@ private:
std::unique_ptr<VulkanDevice> device;
static const int SceneVertexBufferSize = 1 * 1024 * 1024;
std::unique_ptr<VulkanBuffer> sceneVertexBuffer;
SceneVertex* sceneVertices = nullptr;
int sceneVertexPos = 0;
std::unique_ptr<VulkanBuffer> vertexBuffer;
std::unique_ptr<VulkanBuffer> indexBuffer;
std::unique_ptr<VulkanBuffer> transferBuffer;