Change lightmapper to only use one image

This commit is contained in:
Magnus Norddahl 2023-09-15 18:07:37 +02:00 committed by Christoph Oelckers
parent 644500de61
commit 39312e898f
6 changed files with 205 additions and 279 deletions

View file

@ -54,6 +54,7 @@ struct LevelMeshSurface
int texHeight = 0;
bool needsUpdate = true;
bool alreadyInVisibleList = false;
//
// Required for internal lightmapper:
@ -83,18 +84,8 @@ struct LevelMeshSurface
//
inline uint32_t Area() const { return texWidth * texHeight; }
//
// VkLightmap extra stuff that I dislike:
//
TArray<FVector3> verts;
// Touching light sources
std::vector<const LevelMeshLight*> LightList;
// Lightmapper has a lot of additional padding around the borders
int lightmapperAtlasPage = -1;
int lightmapperAtlasX = -1;
int lightmapperAtlasY = -1;
};
inline float IsInFrontOfPlane(const FVector4& plane, const FVector3& point)

View file

@ -39,6 +39,7 @@ VkLightmap::VkLightmap(VulkanRenderDevice* fb) : fb(fb)
CreateRaytracePipeline();
CreateResolvePipeline();
CreateBlurPipeline();
CreateBakeImage();
}
VkLightmap::~VkLightmap()
@ -71,38 +72,14 @@ void VkLightmap::Raytrace(const TArray<LevelMeshSurface*>& surfaces)
lightmapRaytrace.Clock();
lightmapRaytraceLast.ResetAndClock();
CreateAtlasImages(surfaces);
uint32_t pixels = 0;
for (auto& surface : surfaces)
SelectSurfaces(surfaces);
if (selectedSurfaces.Size() > 0)
{
surface->needsUpdate = false; // it may have been set to false already, but lightmapper ultimately decides so
pixels += surface->Area();
}
UploadUniforms();
lastSurfaceCount = surfaces.Size();
lastPixelCount = pixels;
totalPixelCount += pixels;
for (size_t pageIndex = 0; pageIndex < atlasImages.size(); pageIndex++)
{
if (atlasImages[pageIndex].pageMaxX && atlasImages[pageIndex].pageMaxY)
{
RenderAtlasImage(pageIndex, surfaces);
}
}
for (size_t pageIndex = 0; pageIndex < atlasImages.size(); pageIndex++)
{
if (atlasImages[pageIndex].pageMaxX && atlasImages[pageIndex].pageMaxY)
{
ResolveAtlasImage(pageIndex);
BlurAtlasImage(pageIndex);
CopyAtlasImageResult(pageIndex, surfaces);
}
UploadUniforms();
RenderBakeImage();
ResolveBakeImage();
BlurBakeImage();
CopyBakeImageResult();
}
lightmapRaytrace.Unclock();
@ -110,45 +87,79 @@ void VkLightmap::Raytrace(const TArray<LevelMeshSurface*>& surfaces)
}
}
void VkLightmap::RenderAtlasImage(size_t pageIndex, const TArray<LevelMeshSurface*>& surfaces)
void VkLightmap::SelectSurfaces(const TArray<LevelMeshSurface*>& surfaces)
{
LightmapImage& img = atlasImages[pageIndex];
bakeImage.maxX = 0;
bakeImage.maxY = 0;
selectedSurfaces.Clear();
// Begin with clear
{
auto cmdbuffer = fb->GetCommands()->GetTransferCommands();
RenderPassBegin()
.RenderPass(raytrace.renderPassBegin.get())
.RenderArea(0, 0, atlasImageSize, atlasImageSize)
.Framebuffer(img.raytrace.Framebuffer.get())
.AddClearColor(0.0f, 0.0f, 0.0f, 0.0f)
.Execute(cmdbuffer);
VkDeviceSize offset = 0;
cmdbuffer->bindVertexBuffers(0, 1, &vertices.Buffer->buffer, &offset);
cmdbuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, raytrace.pipeline.get());
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, raytrace.pipelineLayout.get(), 0, raytrace.descriptorSet0.get());
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, raytrace.pipelineLayout.get(), 1, raytrace.descriptorSet1.get());
}
const int spacing = 3; // Note: the spacing is here to avoid that the resolve sampler finds data from other surface tiles
RectPacker packer(bakeImageSize, bakeImageSize, RectPacker::Spacing(spacing));
for (int i = 0, count = surfaces.Size(); i < count; i++)
{
LevelMeshSurface* targetSurface = surfaces[i];
if (targetSurface->lightmapperAtlasPage != pageIndex)
continue;
LevelMeshSurface* surface = surfaces[i];
if (targetSurface->LightList.empty() && (targetSurface->plane.XYZ() | mesh->SunDirection) < 0.0f) // No lights, no sun //TODO fill the area with black pixels skipping blur and resolve pass
// All surfaces needs to be updated until we rendered them.
surface->needsUpdate = true;
// Only grab surfaces until our bake texture is full
auto result = packer.insert(surface->texWidth + 2, surface->texHeight + 2);
if (result.pageIndex == 0)
{
SelectedSurface selected;
selected.Surface = surface;
selected.X = result.pos.x + 1;
selected.Y = result.pos.y + 1;
selectedSurfaces.Push(selected);
bakeImage.maxX = std::max<uint16_t>(bakeImage.maxX, uint16_t(selected.X + surface->texWidth + spacing));
bakeImage.maxY = std::max<uint16_t>(bakeImage.maxY, uint16_t(selected.Y + surface->texHeight + spacing));
}
}
}
void VkLightmap::RenderBakeImage()
{
auto cmdbuffer = fb->GetCommands()->GetTransferCommands();
RenderPassBegin()
.RenderPass(raytrace.renderPass.get())
.RenderArea(0, 0, bakeImageSize, bakeImageSize)
.Framebuffer(bakeImage.raytrace.Framebuffer.get())
.AddClearColor(0.0f, 0.0f, 0.0f, 0.0f)
.Execute(cmdbuffer);
VkDeviceSize offset = 0;
cmdbuffer->bindVertexBuffers(0, 1, &vertices.Buffer->buffer, &offset);
cmdbuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, raytrace.pipeline.get());
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, raytrace.pipelineLayout.get(), 0, raytrace.descriptorSet0.get());
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, raytrace.pipelineLayout.get(), 1, raytrace.descriptorSet1.get());
lights.Pos = 0;
vertices.Pos = 0;
for (int i = 0, count = selectedSurfaces.Size(); i < count; i++)
{
auto& selectedSurface = selectedSurfaces[i];
LevelMeshSurface* targetSurface = selectedSurface.Surface;
if (targetSurface->LightList.empty() && (targetSurface->plane.XYZ() | mesh->SunDirection) < 0.0f) // No lights, no sun
{
selectedSurface.Rendered = true;
continue;
}
VkViewport viewport = {};
viewport.maxDepth = 1;
viewport.x = (float)targetSurface->lightmapperAtlasX - 1;
viewport.y = (float)targetSurface->lightmapperAtlasY - 1;
viewport.x = (float)selectedSurface.X - 1;
viewport.y = (float)selectedSurface.Y - 1;
viewport.width = (float)(targetSurface->texWidth + 2);
viewport.height = (float)(targetSurface->texHeight + 2);
fb->GetCommands()->GetTransferCommands()->setViewport(0, 1, &viewport);
bool buffersFull = false;
// Paint all surfaces part of the smoothing group into the surface
for (LevelMeshSurface* surface : mesh->SmoothingGroups[targetSurface->smoothingGroupIndex].surfaces)
{
@ -157,46 +168,18 @@ void VkLightmap::RenderAtlasImage(size_t pageIndex, const TArray<LevelMeshSurfac
if (surface != targetSurface && (maxUV.X < 0.0f || maxUV.Y < 0.0f || minUV.X > 1.0f || minUV.Y > 1.0f))
continue; // Bounding box not visible
int firstLight = lights.Pos;
int firstVertex = vertices.Pos;
int lightCount = (int)surface->LightList.size();
int vertexCount = (int)surface->verts.Size();
int vertexCount = surface->numVerts;
if (lights.Pos + lightCount > lights.BufferSize || vertices.Pos + vertexCount > vertices.BufferSize)
{
// Flush scene buffers
fb->GetCommands()->GetTransferCommands()->endRenderPass();
fb->GetCommands()->WaitForCommands(false);
lights.Pos = 0;
vertices.Pos = 0;
firstLight = 0;
firstVertex = 0;
// Begin without clear
auto cmdbuffer = fb->GetCommands()->GetTransferCommands();
RenderPassBegin()
.RenderPass(raytrace.renderPassContinue.get())
.RenderArea(0, 0, atlasImageSize, atlasImageSize)
.Framebuffer(img.raytrace.Framebuffer.get())
.Execute(cmdbuffer);
VkDeviceSize offset = 0;
cmdbuffer->bindVertexBuffers(0, 1, &vertices.Buffer->buffer, &offset);
cmdbuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, raytrace.pipeline.get());
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, raytrace.pipelineLayout.get(), 0, raytrace.descriptorSet0.get());
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, raytrace.pipelineLayout.get(), 1, raytrace.descriptorSet1.get());
cmdbuffer->setViewport(0, 1, &viewport);
if (lights.Pos + lightCount > lights.BufferSize)
{
throw std::runtime_error("SceneLightBuffer is too small!");
}
else if (vertices.Pos + vertexCount > vertices.BufferSize)
{
throw std::runtime_error("SceneVertexBuffer is too small!");
}
// Our vertex or light buffer is full. Postpone the rest.
buffersFull = true;
break;
}
int firstLight = lights.Pos;
int firstVertex = vertices.Pos;
lights.Pos += lightCount;
vertices.Pos += vertexCount;
@ -229,57 +212,29 @@ void VkLightmap::RenderAtlasImage(size_t pageIndex, const TArray<LevelMeshSurfac
{
for (int idx = 0; idx < vertexCount; idx++)
{
(vertex++)->Position = ToUV(surface->verts[idx], targetSurface);
(vertex++)->Position = ToUV(mesh->MeshVertices[surface->startVertIndex + idx], targetSurface);
}
}
else
{
(vertex++)->Position = ToUV(surface->verts[0], targetSurface);
(vertex++)->Position = ToUV(surface->verts[2], targetSurface);
(vertex++)->Position = ToUV(surface->verts[3], targetSurface);
(vertex++)->Position = ToUV(surface->verts[1], targetSurface);
(vertex++)->Position = ToUV(mesh->MeshVertices[surface->startVertIndex + 0], targetSurface);
(vertex++)->Position = ToUV(mesh->MeshVertices[surface->startVertIndex + 2], targetSurface);
(vertex++)->Position = ToUV(mesh->MeshVertices[surface->startVertIndex + 3], targetSurface);
(vertex++)->Position = ToUV(mesh->MeshVertices[surface->startVertIndex + 1], targetSurface);
}
fb->GetCommands()->GetTransferCommands()->draw(vertexCount, 1, firstVertex, 0);
}
if (buffersFull)
break;
selectedSurface.Rendered = true;
}
fb->GetCommands()->GetTransferCommands()->endRenderPass();
}
void VkLightmap::CreateAtlasImages(const TArray<LevelMeshSurface*>& surfaces)
{
for (auto& page : atlasImages)
{
page.pageMaxX = 0;
page.pageMaxY = 0;
}
const int spacing = 3; // Note: the spacing is here to avoid that the resolve sampler finds data from other surface tiles
RectPacker packer(atlasImageSize, atlasImageSize, RectPacker::Spacing(spacing));
size_t pageIndex = atlasImages.size();
for (int i = 0, count = surfaces.Size(); i < count; i++)
{
LevelMeshSurface* surface = surfaces[i];
auto result = packer.insert(surface->texWidth + 2, surface->texHeight + 2);
surface->lightmapperAtlasX = result.pos.x + 1;
surface->lightmapperAtlasY = result.pos.y + 1;
surface->lightmapperAtlasPage = (int)result.pageIndex;
for (;pageIndex <= result.pageIndex; pageIndex++)
{
atlasImages.push_back(CreateImage(atlasImageSize, atlasImageSize));
}
auto& image = atlasImages[result.pageIndex];
image.pageMaxX = std::max<uint16_t>(image.pageMaxX, uint16_t(surface->lightmapperAtlasX + surface->texWidth + spacing));
image.pageMaxY = std::max<uint16_t>(image.pageMaxY, uint16_t(surface->lightmapperAtlasY + surface->texHeight + spacing));
}
}
void VkLightmap::UploadUniforms()
{
Uniforms values = {};
@ -298,31 +253,29 @@ void VkLightmap::UploadUniforms()
.Execute(cmdbuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
}
void VkLightmap::ResolveAtlasImage(size_t pageIndex)
void VkLightmap::ResolveBakeImage()
{
LightmapImage& img = atlasImages[pageIndex];
auto cmdbuffer = fb->GetCommands()->GetTransferCommands();
PipelineBarrier()
.AddImage(img.raytrace.Image.get(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT)
.AddImage(bakeImage.raytrace.Image.get(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT)
.Execute(cmdbuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
RenderPassBegin()
.RenderPass(resolve.renderPass.get())
.RenderArea(0, 0, img.pageMaxX, img.pageMaxY)
.Framebuffer(img.resolve.Framebuffer.get())
.RenderArea(0, 0, bakeImage.maxX, bakeImage.maxY)
.Framebuffer(bakeImage.resolve.Framebuffer.get())
.Execute(cmdbuffer);
VkDeviceSize offset = 0;
cmdbuffer->bindVertexBuffers(0, 1, &vertices.Buffer->buffer, &offset);
cmdbuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, resolve.pipeline.get());
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, resolve.pipelineLayout.get(), 0, img.resolve.DescriptorSet.get());
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, resolve.pipelineLayout.get(), 0, bakeImage.resolve.DescriptorSet.get());
VkViewport viewport = {};
viewport.maxDepth = 1;
viewport.width = (float)img.pageMaxX;
viewport.height = (float)img.pageMaxY;
viewport.width = (float)bakeImage.maxX;
viewport.height = (float)bakeImage.maxY;
cmdbuffer->setViewport(0, 1, &viewport);
LightmapPushConstants pc;
@ -347,33 +300,31 @@ void VkLightmap::ResolveAtlasImage(size_t pageIndex)
cmdbuffer->endRenderPass();
}
void VkLightmap::BlurAtlasImage(size_t pageIndex)
void VkLightmap::BlurBakeImage()
{
LightmapImage& img = atlasImages[pageIndex];
auto cmdbuffer = fb->GetCommands()->GetTransferCommands();
PipelineBarrier()
.AddImage(img.resolve.Image.get(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT)
.AddImage(bakeImage.resolve.Image.get(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT)
.Execute(cmdbuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
// Pass 0
{
RenderPassBegin()
.RenderPass(blur.renderPass.get())
.RenderArea(0, 0, img.pageMaxX, img.pageMaxY)
.Framebuffer(img.blur.Framebuffer.get())
.RenderArea(0, 0, bakeImage.maxX, bakeImage.maxY)
.Framebuffer(bakeImage.blur.Framebuffer.get())
.Execute(cmdbuffer);
VkDeviceSize offset = 0;
cmdbuffer->bindVertexBuffers(0, 1, &vertices.Buffer->buffer, &offset);
cmdbuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, blur.pipeline[0].get());
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, blur.pipelineLayout.get(), 0, img.blur.DescriptorSet[0].get());
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, blur.pipelineLayout.get(), 0, bakeImage.blur.DescriptorSet[0].get());
VkViewport viewport = {};
viewport.maxDepth = 1;
viewport.width = (float)img.pageMaxX;
viewport.height = (float)img.pageMaxY;
viewport.width = (float)bakeImage.maxX;
viewport.height = (float)bakeImage.maxY;
cmdbuffer->setViewport(0, 1, &viewport);
LightmapPushConstants pc;
@ -399,26 +350,26 @@ void VkLightmap::BlurAtlasImage(size_t pageIndex)
}
PipelineBarrier()
.AddImage(img.blur.Image.get(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT)
.AddImage(bakeImage.blur.Image.get(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT)
.Execute(cmdbuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
// Pass 1 (outputs back into resolve fb)
{
RenderPassBegin()
.RenderPass(blur.renderPass.get())
.RenderArea(0, 0, img.pageMaxX, img.pageMaxY)
.Framebuffer(img.resolve.Framebuffer.get())
.RenderArea(0, 0, bakeImage.maxX, bakeImage.maxY)
.Framebuffer(bakeImage.resolve.Framebuffer.get())
.Execute(cmdbuffer);
VkDeviceSize offset = 0;
cmdbuffer->bindVertexBuffers(0, 1, &vertices.Buffer->buffer, &offset);
cmdbuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, blur.pipeline[1].get());
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, blur.pipelineLayout.get(), 0, img.blur.DescriptorSet[1].get());
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, blur.pipelineLayout.get(), 0, bakeImage.blur.DescriptorSet[1].get());
VkViewport viewport = {};
viewport.maxDepth = 1;
viewport.width = (float)img.pageMaxX;
viewport.height = (float)img.pageMaxY;
viewport.width = (float)bakeImage.maxX;
viewport.height = (float)bakeImage.maxY;
cmdbuffer->setViewport(0, 1, &viewport);
LightmapPushConstants pc;
@ -444,47 +395,61 @@ void VkLightmap::BlurAtlasImage(size_t pageIndex)
}
}
void VkLightmap::CopyAtlasImageResult(size_t pageIndex, const TArray<LevelMeshSurface*>& surfaces)
void VkLightmap::CopyBakeImageResult()
{
LightmapImage& img = atlasImages[pageIndex];
uint32_t pixels = 0;
std::set<int> seenPages;
std::vector<VkImageCopy> regions;
for (int i = 0, count = surfaces.Size(); i < count; i++)
for (int i = 0, count = selectedSurfaces.Size(); i < count; i++)
{
LevelMeshSurface* surface = surfaces[i];
if (surface->lightmapperAtlasPage != pageIndex)
continue;
auto& selected = selectedSurfaces[i];
if (selected.Rendered)
{
LevelMeshSurface* surface = selected.Surface;
VkImageCopy region = {};
region.srcOffset.x = selected.X;
region.srcOffset.y = selected.Y;
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.srcSubresource.layerCount = 1;
region.dstOffset.x = surface->atlasX;
region.dstOffset.y = surface->atlasY;
region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.dstSubresource.layerCount = 1;
region.dstSubresource.baseArrayLayer = surface->atlasPageIndex;
region.extent.width = surface->texWidth;
region.extent.height = surface->texHeight;
region.extent.depth = 1;
regions.push_back(region);
seenPages.insert(surface->atlasPageIndex);
VkImageCopy region = {};
region.srcOffset.x = surface->lightmapperAtlasX;
region.srcOffset.y = surface->lightmapperAtlasY;
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.srcSubresource.layerCount = 1;
region.dstOffset.x = surface->atlasX;
region.dstOffset.y = surface->atlasY;
region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.dstSubresource.layerCount = 1;
region.dstSubresource.baseArrayLayer = surface->atlasPageIndex;
region.extent.width = surface->texWidth;
region.extent.height = surface->texHeight;
region.extent.depth = 1;
regions.push_back(region);
// We rendered this surface. Does not need an update anymore.
surface->needsUpdate = false;
pixels += surface->Area();
lastSurfaceCount++;
}
}
lastPixelCount = pixels;
totalPixelCount += pixels;
if (!regions.empty())
{
auto cmdbuffer = fb->GetCommands()->GetTransferCommands();
PipelineBarrier()
.AddImage(img.resolve.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)
.AddImage(fb->GetTextureManager()->Lightmap.Image.get(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, (int)pageIndex, 1)
.Execute(cmdbuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
PipelineBarrier barrier0;
barrier0.AddImage(bakeImage.resolve.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);
for (int pageIndex : seenPages)
barrier0.AddImage(fb->GetTextureManager()->Lightmap.Image.get(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, pageIndex, 1);
barrier0.Execute(cmdbuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
cmdbuffer->copyImage(img.resolve.Image->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, fb->GetTextureManager()->Lightmap.Image->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, (uint32_t)regions.size(), regions.data());
cmdbuffer->copyImage(bakeImage.resolve.Image->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, fb->GetTextureManager()->Lightmap.Image->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, (uint32_t)regions.size(), regions.data());
PipelineBarrier()
.AddImage(fb->GetTextureManager()->Lightmap.Image.get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, (int)pageIndex, 1)
.Execute(cmdbuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
PipelineBarrier barrier1;
for (int pageIndex : seenPages)
barrier1.AddImage(fb->GetTextureManager()->Lightmap.Image.get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, pageIndex, 1);
barrier1.Execute(cmdbuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
}
}
@ -584,7 +549,7 @@ void VkLightmap::CreateRaytracePipeline()
.DebugName("raytrace.pipelineLayout")
.Create(fb->GetDevice());
raytrace.renderPassBegin = RenderPassBuilder()
raytrace.renderPass = RenderPassBuilder()
.AddAttachment(
VK_FORMAT_R16G16B16A16_SFLOAT,
VK_SAMPLE_COUNT_4_BIT,
@ -599,30 +564,12 @@ void VkLightmap::CreateRaytracePipeline()
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT)
.DebugName("raytrace.renderPassBegin")
.Create(fb->GetDevice());
raytrace.renderPassContinue = RenderPassBuilder()
.AddAttachment(
VK_FORMAT_R16G16B16A16_SFLOAT,
VK_SAMPLE_COUNT_4_BIT,
VK_ATTACHMENT_LOAD_OP_LOAD,
VK_ATTACHMENT_STORE_OP_STORE,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)
.AddSubpass()
.AddSubpassColorAttachmentRef(0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)
.AddExternalSubpassDependency(
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT)
.DebugName("raytrace.renderPassContinue")
.DebugName("raytrace.renderPass")
.Create(fb->GetDevice());
raytrace.pipeline = GraphicsPipelineBuilder()
.Layout(raytrace.pipelineLayout.get())
.RenderPass(raytrace.renderPassBegin.get())
.RenderPass(raytrace.renderPass.get())
.AddVertexShader(shaders.vert.get())
.AddFragmentShader(shaders.fragRaytrace.get())
.AddVertexBufferBinding(0, sizeof(SceneVertex))
@ -808,11 +755,12 @@ void VkLightmap::CreateBlurPipeline()
.Create(fb->GetDevice());
}
LightmapImage VkLightmap::CreateImage(int width, int height)
void VkLightmap::CreateBakeImage()
{
LightmapImage img;
int width = bakeImageSize;
int height = bakeImageSize;
img.raytrace.Image = ImageBuilder()
bakeImage.raytrace.Image = ImageBuilder()
.Usage(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT)
.Format(VK_FORMAT_R16G16B16A16_SFLOAT)
.Size(width, height)
@ -820,73 +768,71 @@ LightmapImage VkLightmap::CreateImage(int width, int height)
.DebugName("LightmapImage.raytrace.Image")
.Create(fb->GetDevice());
img.raytrace.View = ImageViewBuilder()
.Image(img.raytrace.Image.get(), VK_FORMAT_R16G16B16A16_SFLOAT)
bakeImage.raytrace.View = ImageViewBuilder()
.Image(bakeImage.raytrace.Image.get(), VK_FORMAT_R16G16B16A16_SFLOAT)
.DebugName("LightmapImage.raytrace.View")
.Create(fb->GetDevice());
img.raytrace.Framebuffer = FramebufferBuilder()
.RenderPass(raytrace.renderPassBegin.get())
bakeImage.raytrace.Framebuffer = FramebufferBuilder()
.RenderPass(raytrace.renderPass.get())
.Size(width, height)
.AddAttachment(img.raytrace.View.get())
.AddAttachment(bakeImage.raytrace.View.get())
.DebugName("LightmapImage.raytrace.Framebuffer")
.Create(fb->GetDevice());
img.resolve.Image = ImageBuilder()
bakeImage.resolve.Image = ImageBuilder()
.Usage(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT)
.Format(VK_FORMAT_R16G16B16A16_SFLOAT)
.Size(width, height)
.DebugName("LightmapImage.resolve.Image")
.Create(fb->GetDevice());
img.resolve.View = ImageViewBuilder()
.Image(img.resolve.Image.get(), VK_FORMAT_R16G16B16A16_SFLOAT)
bakeImage.resolve.View = ImageViewBuilder()
.Image(bakeImage.resolve.Image.get(), VK_FORMAT_R16G16B16A16_SFLOAT)
.DebugName("LightmapImage.resolve.View")
.Create(fb->GetDevice());
img.resolve.Framebuffer = FramebufferBuilder()
bakeImage.resolve.Framebuffer = FramebufferBuilder()
.RenderPass(resolve.renderPass.get())
.Size(width, height)
.AddAttachment(img.resolve.View.get())
.AddAttachment(bakeImage.resolve.View.get())
.DebugName("LightmapImage.resolve.Framebuffer")
.Create(fb->GetDevice());
img.resolve.DescriptorSet = resolve.descriptorPool->allocate(resolve.descriptorSetLayout.get());
img.resolve.DescriptorSet->SetDebugName("resolve.descriptorSet");
bakeImage.resolve.DescriptorSet = resolve.descriptorPool->allocate(resolve.descriptorSetLayout.get());
bakeImage.resolve.DescriptorSet->SetDebugName("resolve.descriptorSet");
img.blur.Image = ImageBuilder()
bakeImage.blur.Image = ImageBuilder()
.Usage(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT)
.Format(VK_FORMAT_R16G16B16A16_SFLOAT)
.Size(width, height)
.DebugName("LightmapImage.blur.Image")
.Create(fb->GetDevice());
img.blur.View = ImageViewBuilder()
.Image(img.blur.Image.get(), VK_FORMAT_R16G16B16A16_SFLOAT)
bakeImage.blur.View = ImageViewBuilder()
.Image(bakeImage.blur.Image.get(), VK_FORMAT_R16G16B16A16_SFLOAT)
.DebugName("LightmapImage.blur.View")
.Create(fb->GetDevice());
img.blur.Framebuffer = FramebufferBuilder()
bakeImage.blur.Framebuffer = FramebufferBuilder()
.RenderPass(blur.renderPass.get())
.Size(width, height)
.AddAttachment(img.blur.View.get())
.AddAttachment(bakeImage.blur.View.get())
.DebugName("LightmapImage.blur.Framebuffer")
.Create(fb->GetDevice());
img.blur.DescriptorSet[0] = blur.descriptorPool->allocate(blur.descriptorSetLayout.get());
img.blur.DescriptorSet[0]->SetDebugName("blur.descriptorSet");
bakeImage.blur.DescriptorSet[0] = blur.descriptorPool->allocate(blur.descriptorSetLayout.get());
bakeImage.blur.DescriptorSet[0]->SetDebugName("blur.descriptorSet");
img.blur.DescriptorSet[1] = blur.descriptorPool->allocate(blur.descriptorSetLayout.get());
img.blur.DescriptorSet[1]->SetDebugName("blur.descriptorSet");
bakeImage.blur.DescriptorSet[1] = blur.descriptorPool->allocate(blur.descriptorSetLayout.get());
bakeImage.blur.DescriptorSet[1]->SetDebugName("blur.descriptorSet");
WriteDescriptors()
.AddCombinedImageSampler(img.resolve.DescriptorSet.get(), 0, img.raytrace.View.get(), resolve.sampler.get(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
.AddCombinedImageSampler(img.blur.DescriptorSet[0].get(), 0, img.resolve.View.get(), blur.sampler.get(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
.AddCombinedImageSampler(img.blur.DescriptorSet[1].get(), 0, img.blur.View.get(), blur.sampler.get(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
.AddCombinedImageSampler(bakeImage.resolve.DescriptorSet.get(), 0, bakeImage.raytrace.View.get(), resolve.sampler.get(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
.AddCombinedImageSampler(bakeImage.blur.DescriptorSet[0].get(), 0, bakeImage.resolve.View.get(), blur.sampler.get(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
.AddCombinedImageSampler(bakeImage.blur.DescriptorSet[1].get(), 0, bakeImage.blur.View.get(), blur.sampler.get(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
.Execute(fb->GetDevice());
return img;
}
void VkLightmap::CreateUniformBuffer()

View file

@ -31,7 +31,7 @@ struct LightmapPushConstants
float PushPadding4;
};
struct LightmapImage
struct LightmapBakeImage
{
struct
{
@ -56,9 +56,9 @@ struct LightmapImage
std::unique_ptr<VulkanDescriptorSet> DescriptorSet[2];
} blur;
// how much of the page is used?
uint16_t pageMaxX = 0;
uint16_t pageMaxY = 0;
// how much of the image is used for the baking
uint16_t maxX = 0;
uint16_t maxY = 0;
};
struct SceneVertex
@ -82,6 +82,14 @@ struct LightInfo
float Padding3;
};
struct SelectedSurface
{
LevelMeshSurface* Surface = nullptr;
int X = -1;
int Y = -1;
bool Rendered = false;
};
static_assert(sizeof(LightInfo) == sizeof(float) * 20);
class VkLightmap
@ -92,17 +100,16 @@ public:
void Raytrace(const TArray<LevelMeshSurface*>& surfaces);
void SetLevelMesh(LevelMesh* level);
private:
void UpdateAccelStructDescriptors();
void SelectSurfaces(const TArray<LevelMeshSurface*>& surfaces);
void UploadUniforms();
void CreateAtlasImages(const TArray<LevelMeshSurface*>& surfaces);
void RenderAtlasImage(size_t pageIndex, const TArray<LevelMeshSurface*>& surfaces);
void ResolveAtlasImage(size_t pageIndex);
void BlurAtlasImage(size_t pageIndex);
void CopyAtlasImageResult(size_t pageIndex, const TArray<LevelMeshSurface*>& surfaces);
void RenderBakeImage();
void ResolveBakeImage();
void BlurBakeImage();
void CopyBakeImageResult();
LightmapImage CreateImage(int width, int height);
void UpdateAccelStructDescriptors();
void CreateShaders();
void CreateRaytracePipeline();
@ -111,6 +118,7 @@ private:
void CreateUniformBuffer();
void CreateSceneVertexBuffer();
void CreateSceneLightBuffer();
void CreateBakeImage();
static FVector2 ToUV(const FVector3& vert, const LevelMeshSurface* targetSurface);
@ -121,6 +129,8 @@ private:
bool useRayQuery = true;
TArray<SelectedSurface> selectedSurfaces;
struct
{
std::unique_ptr<VulkanBuffer> Buffer;
@ -162,8 +172,7 @@ private:
std::unique_ptr<VulkanDescriptorSetLayout> descriptorSetLayout1;
std::unique_ptr<VulkanPipelineLayout> pipelineLayout;
std::unique_ptr<VulkanPipeline> pipeline;
std::unique_ptr<VulkanRenderPass> renderPassBegin;
std::unique_ptr<VulkanRenderPass> renderPassContinue;
std::unique_ptr<VulkanRenderPass> renderPass;
std::unique_ptr<VulkanDescriptorPool> descriptorPool0;
std::unique_ptr<VulkanDescriptorPool> descriptorPool1;
std::unique_ptr<VulkanDescriptorSet> descriptorSet0;
@ -190,6 +199,6 @@ private:
std::unique_ptr<VulkanSampler> sampler;
} blur;
std::vector<LightmapImage> atlasImages;
static const int atlasImageSize = 2048;
LightmapBakeImage bakeImage;
static const int bakeImageSize = 2048;
};

View file

@ -221,7 +221,7 @@ void VkTextureManager::CreateLightmap(int newLMTextureSize, int newLMTextureCoun
if (newPixelData.Size() >= (size_t)w * h * count * 3)
{
assert(newPixelData.Size() == w * h * count * 3);
assert(newPixelData.Size() == (size_t)w * h * count * 3);
int totalSize = w * h * count * pixelsize;

View file

@ -476,17 +476,6 @@ void VulkanRenderDevice::BeginFrame()
levelMesh->UpdateLightLists();
GetTextureManager()->CreateLightmap(levelMesh->LMTextureSize, levelMesh->LMTextureCount, std::move(levelMesh->LMTextureData));
GetLightmap()->SetLevelMesh(levelMesh);
#if 0 // full lightmap generation
TArray<LevelMeshSurface*> surfaces;
surfaces.Reserve(mesh->GetSurfaceCount());
for (unsigned i = 0, count = mesh->GetSurfaceCount(); i < count; ++i)
{
surfaces[i] = mesh->GetSurface(i);
}
GetLightmap()->Raytrace(surfaces);
#endif
}
}

View file

@ -1079,15 +1079,6 @@ void DoomLevelMesh::SetupLightmapUvs()
sortedSurfaces.push_back(&surface);
}
// VkLightmapper old ZDRay properties
for (auto& surface : Surfaces)
{
for (int i = 0; i < surface.numVerts; ++i)
{
surface.verts.Push(MeshVertices[surface.startVertIndex + i]);
}
}
BuildSmoothingGroups();
std::sort(sortedSurfaces.begin(), sortedSurfaces.end(), [](LevelMeshSurface* a, LevelMeshSurface* b) { return a->texHeight != b->texHeight ? a->texHeight > b->texHeight : a->texWidth > b->texWidth; });