Use a shader to copy the pixels as vkCmdCopyImage is very slow. Also gives us more flexibility in the future for composite lightmaps

This commit is contained in:
Magnus Norddahl 2023-09-22 01:41:56 +02:00 committed by Christoph Oelckers
parent cf6f349943
commit ae4e50a2d4
8 changed files with 331 additions and 141 deletions

View file

@ -11,16 +11,14 @@
#include "cmdlib.h"
static int lastSurfaceCount;
static glcycle_t lightmapRaytrace;
static glcycle_t lightmapRaytraceLast;
static uint32_t lastPixelCount;
static uint32_t totalPixelCount;
ADD_STAT(lightmapper)
{
FString out;
out.Format("last: %.3fms\ntotal: %3.fms\nLast batch surface count: %d\nLast batch pixel count: %u\nTotal pixel count: %u", lightmapRaytraceLast.TimeMS(), lightmapRaytrace.TimeMS(), lastSurfaceCount, lastPixelCount, totalPixelCount);
out.Format("CPU time: %.3fms\nSurface count: %d\nPixel count: %u K", lightmapRaytraceLast.TimeMS(), lastSurfaceCount, lastPixelCount / 1024);
return out;
}
@ -33,12 +31,14 @@ VkLightmap::VkLightmap(VulkanRenderDevice* fb) : fb(fb)
useRayQuery = fb->GetDevice()->PhysicalDevice.Features.RayQuery.rayQuery;
CreateUniformBuffer();
CreateSceneLightBuffer();
CreateLightBuffer();
CreateTileBuffer();
CreateShaders();
CreateRaytracePipeline();
CreateResolvePipeline();
CreateBlurPipeline();
CreateCopyPipeline();
CreateBakeImage();
}
@ -46,6 +46,8 @@ VkLightmap::~VkLightmap()
{
if (lights.Buffer)
lights.Buffer->Unmap();
if (copytiles.Buffer)
copytiles.Buffer->Unmap();
}
void VkLightmap::SetLevelMesh(LevelMesh* level)
@ -53,9 +55,7 @@ void VkLightmap::SetLevelMesh(LevelMesh* level)
mesh = level;
UpdateAccelStructDescriptors();
lightmapRaytrace.Reset();
lightmapRaytraceLast.Reset();
totalPixelCount = 0;
lastPixelCount = 0;
lastSurfaceCount = 0;
}
@ -70,10 +70,8 @@ void VkLightmap::Raytrace(const TArray<LevelMeshSurface*>& surfaces)
{
if (surfaces.Size())
{
lightmapRaytrace.active = true;
lightmapRaytraceLast.active = true;
lightmapRaytrace.Clock();
lightmapRaytraceLast.ResetAndClock();
SelectSurfaces(surfaces);
@ -82,15 +80,14 @@ void VkLightmap::Raytrace(const TArray<LevelMeshSurface*>& surfaces)
fb->GetCommands()->PushGroup(fb->GetCommands()->GetTransferCommands(), "lightmap.total");
UploadUniforms();
RenderBakeImage();
ResolveBakeImage();
BlurBakeImage();
CopyBakeImageResult();
Render();
Resolve();
Blur();
CopyResult();
fb->GetCommands()->PopGroup(fb->GetCommands()->GetTransferCommands());
}
lightmapRaytrace.Unclock();
lightmapRaytraceLast.Unclock();
}
}
@ -129,7 +126,7 @@ void VkLightmap::SelectSurfaces(const TArray<LevelMeshSurface*>& surfaces)
}
}
void VkLightmap::RenderBakeImage()
void VkLightmap::Render()
{
auto cmdbuffer = fb->GetCommands()->GetTransferCommands();
@ -167,7 +164,7 @@ void VkLightmap::RenderBakeImage()
continue;
}
LightmapPushConstants pc;
LightmapRaytracePC pc;
pc.TileX = (float)selectedSurface.X;
pc.TileY = (float)selectedSurface.Y;
pc.SurfaceIndex = mesh->GetSurfaceIndex(targetSurface);
@ -229,7 +226,7 @@ void VkLightmap::RenderBakeImage()
pc.LightStart = surface->LightListPos;
pc.LightEnd = pc.LightStart + lightCount;
cmdbuffer->pushConstants(raytrace.pipelineLayout.get(), VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(LightmapPushConstants), &pc);
cmdbuffer->pushConstants(raytrace.pipelineLayout.get(), VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(LightmapRaytracePC), &pc);
cmdbuffer->drawIndexed(surface->numElements, 1, surface->startElementIndex, 0, 0);
}
@ -269,7 +266,7 @@ void VkLightmap::UploadUniforms()
.Execute(cmdbuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
}
void VkLightmap::ResolveBakeImage()
void VkLightmap::Resolve()
{
auto cmdbuffer = fb->GetCommands()->GetTransferCommands();
@ -301,7 +298,7 @@ void VkLightmap::ResolveBakeImage()
fb->GetCommands()->PopGroup(cmdbuffer);
}
void VkLightmap::BlurBakeImage()
void VkLightmap::Blur()
{
auto cmdbuffer = fb->GetCommands()->GetTransferCommands();
@ -362,63 +359,127 @@ void VkLightmap::BlurBakeImage()
fb->GetCommands()->PopGroup(cmdbuffer);
}
void VkLightmap::CopyBakeImageResult()
void VkLightmap::CopyResult()
{
// Sort by destination
uint32_t pixels = 0;
lastSurfaceCount = 0;
std::set<int> seenPages;
std::vector<VkImageCopy> regions;
for (auto& list : copylists) list.Clear();
for (int i = 0, count = selectedSurfaces.Size(); i < count; i++)
{
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);
unsigned int pageIndex = (unsigned int)selected.Surface->atlasPageIndex;
if (pageIndex >= copylists.Size())
{
copylists.Resize(pageIndex + 1);
}
copylists[pageIndex].Push(&selected);
pixels += surface->Area();
pixels += selected.Surface->Area();
lastSurfaceCount++;
}
}
lastPixelCount = pixels;
totalPixelCount += pixels;
if (!regions.empty())
if (pixels == 0)
return;
VkTextureImage* destTexture = &fb->GetTextureManager()->Lightmap;
int destSize = fb->GetTextureManager()->LMTextureSize;
auto cmdbuffer = fb->GetCommands()->GetTransferCommands();
fb->GetCommands()->PushGroup(cmdbuffer, "lightmap.copy");
// Transition lightmap destination images to be used as framebuffers and the resolve image as sampling source
PipelineBarrier barrier0;
barrier0.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);
for (unsigned int i = 0, count = copylists.Size(); i < count; i++)
{
auto cmdbuffer = fb->GetCommands()->GetTransferCommands();
fb->GetCommands()->PushGroup(cmdbuffer, "lightmap.copy");
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(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 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);
fb->GetCommands()->PopGroup(cmdbuffer);
if (copylists[i].Size() > 0)
barrier0.AddImage(destTexture->Image.get(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_COLOR_ATTACHMENT_READ_BIT, VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, i, 1);
}
barrier0.Execute(cmdbuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
// Copy into the lightmap images
int start = 0;
int pos = 0;
for (unsigned int i = 0, count = copylists.Size(); i < count; i++)
{
auto& list = copylists[i];
if (list.Size() == 0)
continue;
// Create framebuffer object if it doesn't exist
if (i >= destTexture->LMFramebuffers.size())
{
destTexture->LMFramebuffers.resize(i + 1);
}
auto& framebuffer = destTexture->LMFramebuffers[i];
if (!framebuffer)
{
framebuffer = FramebufferBuilder()
.RenderPass(copy.renderPass.get())
.Size(destSize, destSize)
.AddAttachment(destTexture->View.get())
.DebugName("LMFramebuffer")
.Create(fb->GetDevice());
}
// Copy the tile positions into a storage buffer for the vertex shader to read
start = pos;
for (SelectedSurface* selected : list)
{
LevelMeshSurface* surface = selected->Surface;
CopyTileInfo* copyinfo = &copytiles.Tiles[pos++];
copyinfo->SrcPosX = selected->X;
copyinfo->SrcPosY = selected->Y;
copyinfo->DestPosX = surface->atlasX;
copyinfo->DestPosY = surface->atlasY;
copyinfo->TileWidth = surface->texWidth;
copyinfo->TileHeight = surface->texHeight;
}
// Draw the tiles. One instance per tile.
RenderPassBegin()
.RenderPass(copy.renderPass.get())
.RenderArea(0, 0, destSize, destSize)
.Framebuffer(framebuffer.get())
.Execute(cmdbuffer);
cmdbuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, copy.pipeline.get());
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, copy.pipelineLayout.get(), 0, bakeImage.copy.DescriptorSet.get());
VkViewport viewport = {};
viewport.maxDepth = 1;
viewport.width = (float)destSize;
viewport.height = (float)destSize;
cmdbuffer->setViewport(0, 1, &viewport);
LightmapCopyPC pc;
pc.SrcTexSize = bakeImageSize;
pc.DestTexSize = destSize;
cmdbuffer->pushConstants(copy.pipelineLayout.get(), VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(LightmapCopyPC), &pc);
cmdbuffer->draw(4, pos - start, 0, start);
cmdbuffer->endRenderPass();
}
// Transition lightmap destination images back to be used for fragment shader sampling
PipelineBarrier barrier1;
for (unsigned int i = 0, count = copylists.Size(); i < count; i++)
{
if (copylists[i].Size() > 0)
barrier1.AddImage(destTexture->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, VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, i, 1);
}
barrier1.Execute(cmdbuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
fb->GetCommands()->PopGroup(cmdbuffer);
}
FVector2 VkLightmap::ToUV(const FVector3& vert, const LevelMeshSurface* targetSurface)
@ -454,6 +515,13 @@ void VkLightmap::CreateShaders()
.DebugName("VkLightmap.VertScreenquad")
.Create("VkLightmap.VertScreenquad", fb->GetDevice());
shaders.vertCopy = ShaderBuilder()
.Type(ShaderType::Vertex)
.AddSource("VersionBlock", prefix)
.AddSource("vert_copy.glsl", LoadPrivateShaderLump("shaders/lightmap/vert_copy.glsl").GetChars())
.DebugName("VkLightmap.VertCopy")
.Create("VkLightmap.VertCopy", fb->GetDevice());
shaders.fragRaytrace = ShaderBuilder()
.Type(ShaderType::Fragment)
.AddSource("VersionBlock", traceprefix)
@ -481,6 +549,13 @@ void VkLightmap::CreateShaders()
.AddSource("frag_blur.glsl", LoadPrivateShaderLump("shaders/lightmap/frag_blur.glsl").GetChars())
.DebugName("VkLightmap.FragBlur")
.Create("VkLightmap.FragBlur", fb->GetDevice());
shaders.fragCopy = ShaderBuilder()
.Type(ShaderType::Fragment)
.AddSource("VersionBlock", prefix)
.AddSource("frag_copy.glsl", LoadPrivateShaderLump("shaders/lightmap/frag_copy.glsl").GetChars())
.DebugName("VkLightmap.FragCopy")
.Create("VkLightmap.FragCopy", fb->GetDevice());
}
FString VkLightmap::LoadPrivateShaderLump(const char* lumpname)
@ -524,7 +599,7 @@ void VkLightmap::CreateRaytracePipeline()
.AddSetLayout(raytrace.descriptorSetLayout0.get())
.AddSetLayout(raytrace.descriptorSetLayout1.get())
.AddSetLayout(fb->GetDescriptorSetManager()->GetBindlessSetLayout())
.AddPushConstantRange(VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(LightmapPushConstants))
.AddPushConstantRange(VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(LightmapRaytracePC))
.DebugName("raytrace.pipelineLayout")
.Create(fb->GetDevice());
@ -631,7 +706,6 @@ void VkLightmap::CreateResolvePipeline()
resolve.pipelineLayout = PipelineLayoutBuilder()
.AddSetLayout(resolve.descriptorSetLayout.get())
.AddPushConstantRange(VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(LightmapPushConstants))
.DebugName("resolve.pipelineLayout")
.Create(fb->GetDevice());
@ -666,8 +740,8 @@ void VkLightmap::CreateResolvePipeline()
.Create(fb->GetDevice());
resolve.descriptorPool = DescriptorPoolBuilder()
.AddPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 256)
.MaxSets(256)
.AddPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1)
.MaxSets(1)
.DebugName("resolve.descriptorPool")
.Create(fb->GetDevice());
@ -685,7 +759,6 @@ void VkLightmap::CreateBlurPipeline()
blur.pipelineLayout = PipelineLayoutBuilder()
.AddSetLayout(blur.descriptorSetLayout.get())
.AddPushConstantRange(VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(LightmapPushConstants))
.DebugName("blur.pipelineLayout")
.Create(fb->GetDevice());
@ -723,8 +796,8 @@ void VkLightmap::CreateBlurPipeline()
}
blur.descriptorPool = DescriptorPoolBuilder()
.AddPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 256)
.MaxSets(256)
.AddPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2)
.MaxSets(2)
.DebugName("blur.descriptorPool")
.Create(fb->GetDevice());
@ -733,6 +806,61 @@ void VkLightmap::CreateBlurPipeline()
.Create(fb->GetDevice());
}
void VkLightmap::CreateCopyPipeline()
{
copy.descriptorSetLayout = DescriptorSetLayoutBuilder()
.AddBinding(0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT)
.AddBinding(1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT)
.DebugName("copy.descriptorSetLayout")
.Create(fb->GetDevice());
copy.pipelineLayout = PipelineLayoutBuilder()
.AddSetLayout(copy.descriptorSetLayout.get())
.AddPushConstantRange(VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(LightmapCopyPC))
.DebugName("copy.pipelineLayout")
.Create(fb->GetDevice());
copy.renderPass = RenderPassBuilder()
.AddAttachment(
VK_FORMAT_R16G16B16A16_SFLOAT,
VK_SAMPLE_COUNT_1_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("copy.renderpass")
.Create(fb->GetDevice());
copy.pipeline = GraphicsPipelineBuilder()
.Layout(copy.pipelineLayout.get())
.RenderPass(copy.renderPass.get())
.AddVertexShader(shaders.vertCopy.get())
.AddFragmentShader(shaders.fragCopy.get())
.Topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP)
.AddDynamicState(VK_DYNAMIC_STATE_VIEWPORT)
.Viewport(0.0f, 0.0f, 0.0f, 0.0f)
.Scissor(0, 0, 4096, 4096)
.DebugName("copy.pipeline")
.Create(fb->GetDevice());
copy.descriptorPool = DescriptorPoolBuilder()
.AddPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 256)
.MaxSets(256)
.DebugName("copy.descriptorPool")
.Create(fb->GetDevice());
copy.sampler = SamplerBuilder()
.DebugName("copy.Sampler")
.Create(fb->GetDevice());
}
void VkLightmap::CreateBakeImage()
{
int width = bakeImageSize;
@ -806,10 +934,15 @@ void VkLightmap::CreateBakeImage()
bakeImage.blur.DescriptorSet[1] = blur.descriptorPool->allocate(blur.descriptorSetLayout.get());
bakeImage.blur.DescriptorSet[1]->SetDebugName("blur.descriptorSet");
bakeImage.copy.DescriptorSet = copy.descriptorPool->allocate(copy.descriptorSetLayout.get());
bakeImage.copy.DescriptorSet->SetDebugName("copy.descriptorSet");
WriteDescriptors()
.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)
.AddCombinedImageSampler(bakeImage.copy.DescriptorSet.get(), 0, bakeImage.resolve.View.get(), blur.sampler.get(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
.AddBuffer(bakeImage.copy.DescriptorSet.get(), 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, copytiles.Buffer.get())
.Execute(fb->GetDevice());
}
@ -831,7 +964,7 @@ void VkLightmap::CreateUniformBuffer()
.Create(fb->GetDevice());
}
void VkLightmap::CreateSceneLightBuffer()
void VkLightmap::CreateLightBuffer()
{
size_t size = sizeof(LightInfo) * lights.BufferSize;
@ -849,3 +982,21 @@ void VkLightmap::CreateSceneLightBuffer()
lights.Lights = (LightInfo*)lights.Buffer->Map(0, size);
lights.Pos = 0;
}
void VkLightmap::CreateTileBuffer()
{
size_t size = sizeof(CopyTileInfo) * copytiles.BufferSize;
copytiles.Buffer = 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("CopyTileBuffer")
.Create(fb->GetDevice());
copytiles.Tiles = (CopyTileInfo*)copytiles.Buffer->Map(0, size);
}

View file

@ -17,7 +17,7 @@ struct Uniforms
float SunIntensity;
};
struct LightmapPushConstants
struct LightmapRaytracePC
{
uint32_t LightStart;
uint32_t LightEnd;
@ -35,6 +35,14 @@ struct LightmapPushConstants
float TileHeight;
};
struct LightmapCopyPC
{
int SrcTexSize;
int DestTexSize;
int Padding1;
int Padding2;
};
struct LightmapBakeImage
{
struct
@ -60,6 +68,11 @@ struct LightmapBakeImage
std::unique_ptr<VulkanDescriptorSet> DescriptorSet[2];
} blur;
struct
{
std::unique_ptr<VulkanDescriptorSet> DescriptorSet;
} copy;
// how much of the image is used for the baking
uint16_t maxX = 0;
uint16_t maxY = 0;
@ -91,6 +104,20 @@ struct SelectedSurface
static_assert(sizeof(LightInfo) == sizeof(float) * 20);
struct CopyTileInfo
{
int SrcPosX;
int SrcPosY;
int DestPosX;
int DestPosY;
int TileWidth;
int TileHeight;
int Padding1;
int Padding2;
};
static_assert(sizeof(CopyTileInfo) == sizeof(int32_t) * 8);
class VkLightmap
{
public:
@ -104,10 +131,10 @@ public:
private:
void SelectSurfaces(const TArray<LevelMeshSurface*>& surfaces);
void UploadUniforms();
void RenderBakeImage();
void ResolveBakeImage();
void BlurBakeImage();
void CopyBakeImageResult();
void Render();
void Resolve();
void Blur();
void CopyResult();
void UpdateAccelStructDescriptors();
@ -115,8 +142,10 @@ private:
void CreateRaytracePipeline();
void CreateResolvePipeline();
void CreateBlurPipeline();
void CreateCopyPipeline();
void CreateUniformBuffer();
void CreateSceneLightBuffer();
void CreateLightBuffer();
void CreateTileBuffer();
void CreateBakeImage();
static FVector2 ToUV(const FVector3& vert, const LevelMeshSurface* targetSurface);
@ -129,6 +158,7 @@ private:
bool useRayQuery = true;
TArray<SelectedSurface> selectedSurfaces;
TArray<TArray<SelectedSurface*>> copylists;
struct
{
@ -150,13 +180,22 @@ private:
int ResetCounter = 0;
} lights;
struct
{
const int BufferSize = 100'000;
std::unique_ptr<VulkanBuffer> Buffer;
CopyTileInfo* Tiles = nullptr;
} copytiles;
struct
{
std::unique_ptr<VulkanShader> vertRaytrace;
std::unique_ptr<VulkanShader> vertScreenquad;
std::unique_ptr<VulkanShader> vertCopy;
std::unique_ptr<VulkanShader> fragRaytrace;
std::unique_ptr<VulkanShader> fragResolve;
std::unique_ptr<VulkanShader> fragBlur[2];
std::unique_ptr<VulkanShader> fragCopy;
} shaders;
struct
@ -192,6 +231,16 @@ private:
std::unique_ptr<VulkanSampler> sampler;
} blur;
struct
{
std::unique_ptr<VulkanDescriptorSetLayout> descriptorSetLayout;
std::unique_ptr<VulkanPipelineLayout> pipelineLayout;
std::unique_ptr<VulkanPipeline> pipeline;
std::unique_ptr<VulkanRenderPass> renderPass;
std::unique_ptr<VulkanDescriptorPool> descriptorPool;
std::unique_ptr<VulkanSampler> sampler;
} copy;
LightmapBakeImage bakeImage;
static const int bakeImageSize = 2048;
};

View file

@ -19,6 +19,9 @@ public:
for (auto &it : RSFramebuffers)
deletelist->Add(std::move(it.second));
RSFramebuffers.clear();
for (auto& framebuffer : LMFramebuffers)
deletelist->Add(std::move(framebuffer));
LMFramebuffers.clear();
deletelist->Add(std::move(DepthOnlyView));
deletelist->Add(std::move(View));
deletelist->Add(std::move(Image));
@ -33,6 +36,7 @@ public:
VkImageAspectFlags AspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
std::unique_ptr<VulkanFramebuffer> PPFramebuffer;
std::map<VkRenderPassKey, std::unique_ptr<VulkanFramebuffer>> RSFramebuffers;
std::vector<std::unique_ptr<VulkanFramebuffer>> LMFramebuffers;
};
class VkImageTransition

View file

@ -185,8 +185,7 @@ void VkTextureManager::CreateLightmap()
data.Push(0);
data.Push(0);
data.Push(0);
data.Push(0x3c00); // half-float 1.0
SetLightmap(1, 1, data);
CreateLightmap(1, 1, std::move(data));
}
void VkTextureManager::CreateLightmap(int newLMTextureSize, int newLMTextureCount, TArray<uint16_t>&& newPixelData)
@ -207,7 +206,7 @@ void VkTextureManager::CreateLightmap(int newLMTextureSize, int newLMTextureCoun
Lightmap.Image = ImageBuilder()
.Size(w, h, 1, count)
.Format(VK_FORMAT_R16G16B16A16_SFLOAT)
.Usage(VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT)
.Usage(VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)
.DebugName("VkRenderBuffers.Lightmap")
.Create(fb->GetDevice());
@ -264,66 +263,3 @@ void VkTextureManager::CreateLightmap(int newLMTextureSize, int newLMTextureCoun
.AddImage(&Lightmap, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, false, 0, count)
.Execute(cmdbuffer);
}
void VkTextureManager::SetLightmap(int LMTextureSize, int LMTextureCount, const TArray<uint16_t>& LMTextureData)
{
int w = LMTextureSize;
int h = LMTextureSize;
int count = LMTextureCount;
int pixelsize = 8;
Lightmap.Reset(fb);
Lightmap.Image = ImageBuilder()
.Size(w, h, 1, count)
.Format(VK_FORMAT_R16G16B16A16_SFLOAT)
.Usage(VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT)
.DebugName("VkRenderBuffers.Lightmap")
.Create(fb->GetDevice());
Lightmap.View = ImageViewBuilder()
.Type(VK_IMAGE_VIEW_TYPE_2D_ARRAY)
.Image(Lightmap.Image.get(), VK_FORMAT_R16G16B16A16_SFLOAT)
.DebugName("VkRenderBuffers.LightmapView")
.Create(fb->GetDevice());
auto cmdbuffer = fb->GetCommands()->GetTransferCommands();
int totalSize = w * h * count * pixelsize;
auto stagingBuffer = BufferBuilder()
.Size(totalSize)
.Usage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY)
.DebugName("VkHardwareTexture.mStagingBuffer")
.Create(fb->GetDevice());
uint16_t one = 0x3c00; // half-float 1.0
const uint16_t* src = LMTextureData.Data();
uint16_t* data = (uint16_t*)stagingBuffer->Map(0, totalSize);
for (int i = w * h * count; i > 0; i--)
{
*(data++) = *(src++);
*(data++) = *(src++);
*(data++) = *(src++);
*(data++) = one;
}
stagingBuffer->Unmap();
VkImageTransition()
.AddImage(&Lightmap, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, true, 0, count)
.Execute(cmdbuffer);
VkBufferImageCopy region = {};
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.layerCount = count;
region.imageExtent.depth = 1;
region.imageExtent.width = w;
region.imageExtent.height = h;
cmdbuffer->copyBufferToImage(stagingBuffer->buffer, Lightmap.Image->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
VkImageTransition()
.AddImage(&Lightmap, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, false, 0, count)
.Execute(cmdbuffer);
fb->GetCommands()->TransferDeleteList->Add(std::move(stagingBuffer));
}

View file

@ -24,7 +24,6 @@ public:
void BeginFrame();
void CreateLightmap(int newLMTextureSize, int newLMTextureCount, TArray<uint16_t>&& newPixelData);
void SetLightmap(int LMTextureSize, int LMTextureCount, const TArray<uint16_t>& LMTextureData);
VkTextureImage* GetTexture(const PPTextureType& type, PPTexture* tex);
VkFormat GetTextureFormat(PPTexture* texture);

View file

@ -0,0 +1,10 @@
layout(set = 0, binding = 0) uniform sampler2D Tex;
layout(location = 0) in vec2 TexCoord;
layout(location = 0) out vec4 FragColor;
void main()
{
FragColor = texture(Tex, TexCoord);
}

View file

@ -127,7 +127,7 @@ void main()
}
#if defined(USE_RAYQUERY) // The non-rtx version of TraceFirstHitTriangle is too slow to do AO without the shader getting killed ;(
incoming.rgb *= TraceAmbientOcclusion(origin, normal);
//incoming.rgb *= TraceAmbientOcclusion(origin, normal);
#endif
fragcolor = vec4(incoming, 1.0);

View file

@ -0,0 +1,41 @@
layout(push_constant) uniform PushConstants
{
int SrcTexSize;
int DestTexSize;
int Padding1;
int Padding2;
};
struct TileCopy
{
ivec2 SrcPos;
ivec2 DestPos;
ivec2 TileSize;
int Padding1, Padding2;
};
layout(std430, set = 0, binding = 1) buffer CopyBuffer
{
TileCopy tiles[];
};
layout(location = 0) out vec2 TexCoord;
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()
{
TileCopy tile = tiles[gl_InstanceIndex];
vec2 uv = positions[gl_VertexIndex];
vec2 src = (vec2(tile.SrcPos) + uv * vec2(tile.TileSize)) / float(SrcTexSize);
vec2 dest = (vec2(tile.DestPos) + uv * vec2(tile.TileSize)) / float(DestTexSize);
gl_Position = vec4(dest * 2.0 - 1.0, 0.0, 1.0);
TexCoord = src;
}