#include "vk_lightmapper.h" #include "vulkan/vk_renderdevice.h" #include "vulkan/textures/vk_texture.h" #include "vulkan/commands/vk_commandbuffer.h" #include "vulkan/descriptorsets/vk_descriptorset.h" #include "vulkan/shaders/vk_shadercache.h" #include "vk_levelmesh.h" #include "zvulkan/vulkanbuilders.h" #include "filesystem.h" #include "cmdlib.h" static int lastSurfaceCount; static glcycle_t lightmapRaytraceLast; static uint32_t lastPixelCount; ADD_STAT(lightmapper) { FString out; out.Format("CPU time: %.3fms\nSurface count: %d\nPixel count: %u K", lightmapRaytraceLast.TimeMS(), lastSurfaceCount, lastPixelCount / 1024); return out; } CVAR(Int, lm_background_updates, 8, CVAR_NOSAVE); CVAR(Int, lm_max_updates, 128, CVAR_NOSAVE); CVAR(Float, lm_scale, 1.0, CVAR_NOSAVE); CVAR(Bool, lm_sunlight, true, CVAR_ARCHIVE); CVAR(Bool, lm_blur, true, CVAR_ARCHIVE); CVAR(Bool, lm_ao, true, CVAR_ARCHIVE); CVAR(Bool, lm_softshadows, true, CVAR_ARCHIVE); CVAR(Bool, lm_bounce, true, CVAR_ARCHIVE); CVAR(Bool, lm_dynamic, true, CVAR_ARCHIVE); CVAR(Bool, lm_dynlights, false, CVAR_ARCHIVE); VkLightmapper::VkLightmapper(VulkanRenderDevice* fb) : fb(fb) { useRayQuery = fb->IsRayQueryEnabled(); try { CreateUniformBuffer(); CreateTileBuffer(); CreateDrawIndexedBuffer(); CreateShaders(); CreateRaytracePipeline(); CreateResolvePipeline(); CreateBlurPipeline(); CreateCopyPipeline(); CreateBakeImage(); } catch (...) { ReleaseResources(); throw; } } VkLightmapper::~VkLightmapper() { ReleaseResources(); } void VkLightmapper::ReleaseResources() { if (copytiles.Buffer) copytiles.Buffer->Unmap(); if (drawindexed.CommandsBuffer) drawindexed.CommandsBuffer->Unmap(); if (drawindexed.ConstantsBuffer) drawindexed.ConstantsBuffer->Unmap(); } void VkLightmapper::SetLevelMesh(LevelMesh* level) { mesh = level; UpdateAccelStructDescriptors(); lightmapRaytraceLast.Reset(); lastPixelCount = 0; lastSurfaceCount = 0; } void VkLightmapper::BeginFrame() { drawindexed.Pos = 0; } void VkLightmapper::Raytrace(const TArray& tiles) { if (mesh && tiles.Size() > 0) { lightmapRaytraceLast.active = true; lightmapRaytraceLast.ResetAndClock(); SelectTiles(tiles); if (selectedTiles.Size() > 0) { fb->GetCommands()->PushGroup(fb->GetCommands()->GetTransferCommands(), "lightmap.total"); UploadUniforms(); Render(); Resolve(); if (lm_blur) Blur(); CopyResult(); fb->GetCommands()->PopGroup(fb->GetCommands()->GetTransferCommands()); } lightmapRaytraceLast.Unclock(); } } void VkLightmapper::SelectTiles(const TArray& tiles) { bakeImage.maxX = 0; bakeImage.maxY = 0; selectedTiles.Clear(); // We use a 3 texel spacing between rectangles so that the blur pass will not pick up anything from a neighbour tile. RectPacker packer(bakeImageSize, bakeImageSize, RectPacker::Spacing(3), RectPacker::Padding(3)); for (int i = 0, count = tiles.Size(); i < count; i++) { LightmapTile* tile = tiles[i]; if (!tile->NeedsUpdate) continue; // Only grab surfaces until our bake texture is full auto result = packer.insert(tile->AtlasLocation.Width, tile->AtlasLocation.Height); if (result.pageIndex == 0) { SelectedTile selected; selected.Tile = tile; selected.X = result.pos.x; selected.Y = result.pos.y; selectedTiles.Push(selected); bakeImage.maxX = std::max(bakeImage.maxX, uint16_t(result.pos.x + tile->AtlasLocation.Width)); bakeImage.maxY = std::max(bakeImage.maxY, uint16_t(result.pos.y + tile->AtlasLocation.Height)); tile->NeedsUpdate = false; } } // Include the padding bakeImage.maxX += 3; bakeImage.maxY += 3; } void VkLightmapper::Render() { auto cmdbuffer = fb->GetCommands()->GetTransferCommands(); fb->GetCommands()->PushGroup(cmdbuffer, "lightmap.raytrace"); RenderPassBegin() .RenderPass(raytrace.renderPass.get()) .RenderArea(0, 0, bakeImage.maxX, bakeImage.maxY) .Framebuffer(bakeImage.raytrace.Framebuffer.get()) .AddClearColor(0.0f, 0.0f, 0.0f, 0.0f) .Execute(cmdbuffer); VkDeviceSize offset = 0; cmdbuffer->bindVertexBuffers(0, 1, &fb->GetLevelMesh()->GetVertexBuffer()->buffer, &offset); cmdbuffer->bindIndexBuffer(fb->GetLevelMesh()->GetIndexBuffer()->buffer, 0, VK_INDEX_TYPE_UINT32); cmdbuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, raytrace.pipeline[GetRaytracePipelineIndex()].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->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, raytrace.pipelineLayout.get(), 2, fb->GetDescriptorSetManager()->GetBindlessSet()); VkViewport viewport = {}; viewport.maxDepth = 1; viewport.width = (float)bakeImageSize; viewport.height = (float)bakeImageSize; cmdbuffer->setViewport(0, 1, &viewport); for (int i = 0, count = selectedTiles.Size(); i < count; i++) { auto& selectedTile = selectedTiles[i]; LightmapTile* targetTile = selectedTile.Tile; LightmapRaytracePC pc; pc.TileX = (float)selectedTile.X; pc.TileY = (float)selectedTile.Y; pc.TextureSize = (float)bakeImageSize; pc.TileWidth = (float)targetTile->AtlasLocation.Width; pc.TileHeight = (float)targetTile->AtlasLocation.Height; pc.WorldToLocal = SwapYZ(targetTile->Transform.TranslateWorldToLocal); pc.ProjLocalToU = SwapYZ(targetTile->Transform.ProjLocalToU); pc.ProjLocalToV = SwapYZ(targetTile->Transform.ProjLocalToV); bool buffersFull = false; // Paint all surfaces visible in the tile visibleSurfaces.Clear(); mesh->GetVisibleSurfaces(targetTile, visibleSurfaces); for (int surfaceIndex : visibleSurfaces) { LevelMeshSurface* surface = &mesh->Mesh.Surfaces[surfaceIndex]; pc.SurfaceIndex = surfaceIndex; VkDrawIndexedIndirectCommand cmd; cmd.indexCount = surface->MeshLocation.NumElements; cmd.instanceCount = 1; cmd.firstIndex = surface->MeshLocation.StartElementIndex; cmd.vertexOffset = 0; cmd.firstInstance = drawindexed.Pos; drawindexed.Constants[drawindexed.Pos] = pc; drawindexed.Commands[drawindexed.Pos] = cmd; drawindexed.Pos++; if (drawindexed.Pos == drawindexed.BufferSize) { // Our indirect draw buffer is full. Postpone the rest. buffersFull = true; break; } } if (buffersFull) { while (i < count) { selectedTiles[i].Tile->NeedsUpdate = true; i++; } break; } selectedTile.Rendered = true; } cmdbuffer->drawIndexedIndirect(drawindexed.CommandsBuffer->buffer, 0, drawindexed.Pos, sizeof(VkDrawIndexedIndirectCommand)); cmdbuffer->endRenderPass(); fb->GetCommands()->PopGroup(cmdbuffer); } void VkLightmapper::UploadUniforms() { Uniforms values = {}; values.SunDir = SwapYZ(mesh->SunDirection); values.SunColor = mesh->SunColor; values.SunIntensity = mesh->SunIntensity; uniforms.Uniforms = (uint8_t*)uniforms.TransferBuffer->Map(0, uniforms.NumStructs * uniforms.StructStride); *reinterpret_cast(uniforms.Uniforms + uniforms.StructStride * uniforms.Index) = values; uniforms.TransferBuffer->Unmap(); auto cmdbuffer = fb->GetCommands()->GetTransferCommands(); cmdbuffer->copyBuffer(uniforms.TransferBuffer.get(), uniforms.Buffer.get()); PipelineBarrier() .AddBuffer(uniforms.Buffer.get(), VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT) .Execute(cmdbuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); } void VkLightmapper::Resolve() { auto cmdbuffer = fb->GetCommands()->GetTransferCommands(); fb->GetCommands()->PushGroup(cmdbuffer, "lightmap.resolve"); PipelineBarrier() .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, bakeImage.maxX, bakeImage.maxY) .Framebuffer(bakeImage.resolve.Framebuffer.get()) .Execute(cmdbuffer); cmdbuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, resolve.pipeline.get()); cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, resolve.pipelineLayout.get(), 0, bakeImage.resolve.DescriptorSet.get()); VkViewport viewport = {}; viewport.maxDepth = 1; viewport.width = (float)bakeImage.maxX; viewport.height = (float)bakeImage.maxY; cmdbuffer->setViewport(0, 1, &viewport); cmdbuffer->draw(3, 1, 0, 0); cmdbuffer->endRenderPass(); fb->GetCommands()->PopGroup(cmdbuffer); } void VkLightmapper::Blur() { auto cmdbuffer = fb->GetCommands()->GetTransferCommands(); fb->GetCommands()->PushGroup(cmdbuffer, "lightmap.blur"); PipelineBarrier() .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, bakeImage.maxX, bakeImage.maxY) .Framebuffer(bakeImage.blur.Framebuffer.get()) .Execute(cmdbuffer); cmdbuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, blur.pipeline[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)bakeImage.maxX; viewport.height = (float)bakeImage.maxY; cmdbuffer->setViewport(0, 1, &viewport); cmdbuffer->draw(3, 1, 0, 0); cmdbuffer->endRenderPass(); } PipelineBarrier() .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, bakeImage.maxX, bakeImage.maxY) .Framebuffer(bakeImage.resolve.Framebuffer.get()) .Execute(cmdbuffer); cmdbuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, blur.pipeline[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)bakeImage.maxX; viewport.height = (float)bakeImage.maxY; cmdbuffer->setViewport(0, 1, &viewport); cmdbuffer->draw(3, 1, 0, 0); cmdbuffer->endRenderPass(); } fb->GetCommands()->PopGroup(cmdbuffer); } void VkLightmapper::CopyResult() { // Sort by destination uint32_t pixels = 0; lastSurfaceCount = 0; for (auto& list : copylists) list.Clear(); for (int i = 0, count = selectedTiles.Size(); i < count; i++) { auto& selected = selectedTiles[i]; if (selected.Rendered) { unsigned int pageIndex = (unsigned int)selected.Tile->AtlasLocation.ArrayIndex; if (pageIndex >= copylists.Size()) { copylists.Resize(pageIndex + 1); } copylists[pageIndex].Push(&selected); pixels += selected.Tile->AtlasLocation.Area(); lastSurfaceCount++; } } lastPixelCount = pixels; if (pixels == 0) return; VkTextureImage* destTexture = &fb->GetTextureManager()->Lightmap.Image; int destSize = fb->GetTextureManager()->Lightmap.Size; 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++) { 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->LMViews.resize(i + 1); destTexture->LMFramebuffers.resize(i + 1); } auto& framebuffer = destTexture->LMFramebuffers[i]; if (!framebuffer) { auto& view = destTexture->LMViews[i]; if (!view) { view = ImageViewBuilder() .Type(VK_IMAGE_VIEW_TYPE_2D) .Image(destTexture->Image.get(), VK_FORMAT_R16G16B16A16_SFLOAT, VK_IMAGE_ASPECT_COLOR_BIT, 0, i, 1, 1) .DebugName("LMView") .Create(fb->GetDevice()); } framebuffer = FramebufferBuilder() .RenderPass(copy.renderPass.get()) .Size(destSize, destSize) .AddAttachment(view.get()) .DebugName("LMFramebuffer") .Create(fb->GetDevice()); } // Copy the tile positions into a storage buffer for the vertex shader to read start = pos; for (SelectedTile* selected : list) { LightmapTile* tile = selected->Tile; CopyTileInfo* copyinfo = ©tiles.Tiles[pos++]; copyinfo->SrcPosX = selected->X; copyinfo->SrcPosY = selected->Y; copyinfo->DestPosX = tile->AtlasLocation.X; copyinfo->DestPosY = tile->AtlasLocation.Y; copyinfo->TileWidth = tile->AtlasLocation.Width; copyinfo->TileHeight = tile->AtlasLocation.Height; } // 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); } void VkLightmapper::CreateShaders() { std::string prefix = "#version 460\r\n"; std::string traceprefix = "#version 460\r\n"; prefix += "#extension GL_GOOGLE_include_directive : enable\n"; traceprefix += "#extension GL_GOOGLE_include_directive : enable\n"; traceprefix += "#extension GL_EXT_nonuniform_qualifier : enable\r\n"; if (useRayQuery) { traceprefix += "#extension GL_EXT_ray_query : require\r\n"; traceprefix += "#define USE_RAYQUERY\r\n"; } shaders.vertRaytrace = ShaderBuilder() .Code(CachedGLSLCompiler() .Type(ShaderType::Vertex) .AddSource("VersionBlock", prefix) .AddSource("vert_raytrace.glsl", LoadPrivateShaderLump("shaders/lightmap/vert_raytrace.glsl").GetChars()) .Compile(fb)) .DebugName("VkLightmapper.VertRaytrace") .Create("VkLightmapper.VertRaytrace", fb->GetDevice()); shaders.vertScreenquad = ShaderBuilder() .Code(CachedGLSLCompiler() .Type(ShaderType::Vertex) .AddSource("VersionBlock", prefix) .AddSource("vert_screenquad.glsl", LoadPrivateShaderLump("shaders/lightmap/vert_screenquad.glsl").GetChars()) .Compile(fb)) .DebugName("VkLightmapper.VertScreenquad") .Create("VkLightmapper.VertScreenquad", fb->GetDevice()); shaders.vertCopy = ShaderBuilder() .Code(CachedGLSLCompiler() .Type(ShaderType::Vertex) .AddSource("VersionBlock", prefix) .AddSource("vert_copy.glsl", LoadPrivateShaderLump("shaders/lightmap/vert_copy.glsl").GetChars()) .Compile(fb)) .DebugName("VkLightmapper.VertCopy") .Create("VkLightmapper.VertCopy", fb->GetDevice()); for (int i = 0; i < 16; i++) { std::string defines = traceprefix; if (i & 1) defines += "#define USE_SOFTSHADOWS\n"; if (i & 2) defines += "#define USE_AO\n"; if (i & 4) defines += "#define USE_SUNLIGHT\n"; if (i & 8) defines += "#define USE_BOUNCE\n"; shaders.fragRaytrace[i] = ShaderBuilder() .Code(CachedGLSLCompiler() .Type(ShaderType::Fragment) .AddSource("VersionBlock", defines) .AddSource("frag_raytrace.glsl", LoadPrivateShaderLump("shaders/lightmap/frag_raytrace.glsl").GetChars()) .Compile(fb)) .DebugName("VkLightmapper.FragRaytrace") .Create("VkLightmapper.FragRaytrace", fb->GetDevice()); } shaders.fragResolve = ShaderBuilder() .Code(CachedGLSLCompiler() .Type(ShaderType::Fragment) .AddSource("VersionBlock", prefix) .AddSource("frag_resolve.glsl", LoadPrivateShaderLump("shaders/lightmap/frag_resolve.glsl").GetChars()) .Compile(fb)) .DebugName("VkLightmapper.FragResolve") .Create("VkLightmapper.FragResolve", fb->GetDevice()); shaders.fragBlur[0] = ShaderBuilder() .Code(CachedGLSLCompiler() .Type(ShaderType::Fragment) .AddSource("VersionBlock", prefix + "#define BLUR_HORIZONTAL\r\n") .AddSource("frag_blur.glsl", LoadPrivateShaderLump("shaders/lightmap/frag_blur.glsl").GetChars()) .Compile(fb)) .DebugName("VkLightmapper.FragBlur") .Create("VkLightmapper.FragBlur", fb->GetDevice()); shaders.fragBlur[1] = ShaderBuilder() .Code(CachedGLSLCompiler() .Type(ShaderType::Fragment) .AddSource("VersionBlock", prefix + "#define BLUR_VERTICAL\r\n") .AddSource("frag_blur.glsl", LoadPrivateShaderLump("shaders/lightmap/frag_blur.glsl").GetChars()) .Compile(fb)) .DebugName("VkLightmapper.FragBlur") .Create("VkLightmapper.FragBlur", fb->GetDevice()); shaders.fragCopy = ShaderBuilder() .Code(CachedGLSLCompiler() .Type(ShaderType::Fragment) .AddSource("VersionBlock", prefix) .AddSource("frag_copy.glsl", LoadPrivateShaderLump("shaders/lightmap/frag_copy.glsl").GetChars()) .Compile(fb)) .DebugName("VkLightmapper.FragCopy") .Create("VkLightmapper.FragCopy", fb->GetDevice()); } int VkLightmapper::GetRaytracePipelineIndex() { // When running as the baking tool we don't care about the CVAR or hardware preferences and only want to act on what the map specified. bool userSoftshadows = RunningAsTool || (lm_softshadows && useRayQuery); bool userAO = RunningAsTool || (lm_ao && useRayQuery); bool userSunlight = RunningAsTool || lm_sunlight; bool userBounce = RunningAsTool || lm_bounce; int index = 0; if (userSoftshadows) index |= 1; if (mesh->AmbientOcclusion && userAO) index |= 2; if (mesh->SunColor != FVector3(0.0f, 0.0f, 0.0f) && userSunlight) index |= 4; if (mesh->LightBounce && userBounce) index |= 8; return index; } FString VkLightmapper::LoadPrivateShaderLump(const char* lumpname) { return fb->GetShaderCache()->GetPrivateFile(lumpname).Code; } FString VkLightmapper::LoadPublicShaderLump(const char* lumpname) { return fb->GetShaderCache()->GetPublicFile(lumpname).Code; } void VkLightmapper::CreateRaytracePipeline() { raytrace.descriptorSetLayout0 = DescriptorSetLayoutBuilder() .AddBinding(0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT) .AddBinding(1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT) .AddBinding(2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT) .AddBinding(3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT) .AddBinding(4, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT) .AddBinding(5, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT) .AddBinding(6, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT) .DebugName("raytrace.descriptorSetLayout0") .Create(fb->GetDevice()); if (useRayQuery) { raytrace.descriptorSetLayout1 = DescriptorSetLayoutBuilder() .AddBinding(0, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1, VK_SHADER_STAGE_FRAGMENT_BIT) .AddBinding(1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT) .AddBinding(2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT) .DebugName("raytrace.descriptorSetLayout1") .Create(fb->GetDevice()); } else { raytrace.descriptorSetLayout1 = DescriptorSetLayoutBuilder() .AddBinding(0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT) .AddBinding(1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT) .AddBinding(2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT) .DebugName("raytrace.descriptorSetLayout1") .Create(fb->GetDevice()); } raytrace.pipelineLayout = PipelineLayoutBuilder() .AddSetLayout(raytrace.descriptorSetLayout0.get()) .AddSetLayout(raytrace.descriptorSetLayout1.get()) .AddSetLayout(fb->GetDescriptorSetManager()->GetBindlessLayout()) .DebugName("raytrace.pipelineLayout") .Create(fb->GetDevice()); raytrace.renderPass = RenderPassBuilder() .AddAttachment( VK_FORMAT_R16G16B16A16_SFLOAT, VK_SAMPLE_COUNT_4_BIT, VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_STORE, VK_IMAGE_LAYOUT_UNDEFINED, 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.renderPass") .Create(fb->GetDevice()); for (int i = 0; i < 16; i++) { raytrace.pipeline[i] = GraphicsPipelineBuilder() .Layout(raytrace.pipelineLayout.get()) .RenderPass(raytrace.renderPass.get()) .AddVertexShader(shaders.vertRaytrace.get()) .AddFragmentShader(shaders.fragRaytrace[i].get()) .AddVertexBufferBinding(0, sizeof(FFlatVertex)) .AddVertexAttribute(0, 0, VK_FORMAT_R32G32B32A32_SFLOAT, 0) .Topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST) .AddDynamicState(VK_DYNAMIC_STATE_VIEWPORT) .RasterizationSamples(VK_SAMPLE_COUNT_4_BIT) .Viewport(0.0f, 0.0f, 0.0f, 0.0f) .Scissor(0, 0, 4096, 4096) .DebugName("raytrace.pipeline") .Create(fb->GetDevice()); } raytrace.descriptorPool0 = DescriptorPoolBuilder() .AddPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1) .AddPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 6) .MaxSets(1) .DebugName("raytrace.descriptorPool0") .Create(fb->GetDevice()); if (useRayQuery) { raytrace.descriptorPool1 = DescriptorPoolBuilder() .AddPoolSize(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1) .AddPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 2) .MaxSets(1) .DebugName("raytrace.descriptorPool1") .Create(fb->GetDevice()); } else { raytrace.descriptorPool1 = DescriptorPoolBuilder() .AddPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 3) .MaxSets(1) .DebugName("raytrace.descriptorPool1") .Create(fb->GetDevice()); } raytrace.descriptorSet0 = raytrace.descriptorPool0->allocate(raytrace.descriptorSetLayout0.get()); raytrace.descriptorSet0->SetDebugName("raytrace.descriptorSet1"); raytrace.descriptorSet1 = raytrace.descriptorPool1->allocate(raytrace.descriptorSetLayout1.get()); raytrace.descriptorSet1->SetDebugName("raytrace.descriptorSet1"); } void VkLightmapper::UpdateAccelStructDescriptors() { if (useRayQuery) { WriteDescriptors() .AddAccelerationStructure(raytrace.descriptorSet1.get(), 0, fb->GetLevelMesh()->GetAccelStruct()) .AddBuffer(raytrace.descriptorSet1.get(), 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, fb->GetLevelMesh()->GetVertexBuffer()) .AddBuffer(raytrace.descriptorSet1.get(), 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, fb->GetLevelMesh()->GetIndexBuffer()) .Execute(fb->GetDevice()); } else { WriteDescriptors() .AddBuffer(raytrace.descriptorSet1.get(), 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, fb->GetLevelMesh()->GetNodeBuffer()) .AddBuffer(raytrace.descriptorSet1.get(), 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, fb->GetLevelMesh()->GetVertexBuffer()) .AddBuffer(raytrace.descriptorSet1.get(), 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, fb->GetLevelMesh()->GetIndexBuffer()) .Execute(fb->GetDevice()); } WriteDescriptors() .AddBuffer(raytrace.descriptorSet0.get(), 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, uniforms.Buffer.get(), 0, sizeof(Uniforms)) .AddBuffer(raytrace.descriptorSet0.get(), 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, fb->GetLevelMesh()->GetSurfaceIndexBuffer()) .AddBuffer(raytrace.descriptorSet0.get(), 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, fb->GetLevelMesh()->GetSurfaceBuffer()) .AddBuffer(raytrace.descriptorSet0.get(), 3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, fb->GetLevelMesh()->GetLightBuffer()) .AddBuffer(raytrace.descriptorSet0.get(), 4, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, fb->GetLevelMesh()->GetLightIndexBuffer()) .AddBuffer(raytrace.descriptorSet0.get(), 5, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, fb->GetLevelMesh()->GetPortalBuffer()) .AddBuffer(raytrace.descriptorSet0.get(), 6, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, drawindexed.ConstantsBuffer.get(), 0, drawindexed.BufferSize * sizeof(LightmapRaytracePC)) .Execute(fb->GetDevice()); } void VkLightmapper::CreateResolvePipeline() { resolve.descriptorSetLayout = DescriptorSetLayoutBuilder() .AddBinding(0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT) .DebugName("resolve.descriptorSetLayout") .Create(fb->GetDevice()); resolve.pipelineLayout = PipelineLayoutBuilder() .AddSetLayout(resolve.descriptorSetLayout.get()) .DebugName("resolve.pipelineLayout") .Create(fb->GetDevice()); resolve.renderPass = RenderPassBuilder() .AddAttachment( VK_FORMAT_R16G16B16A16_SFLOAT, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_STORE, VK_IMAGE_LAYOUT_UNDEFINED, 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("resolve.renderpass") .Create(fb->GetDevice()); resolve.pipeline = GraphicsPipelineBuilder() .Layout(resolve.pipelineLayout.get()) .RenderPass(resolve.renderPass.get()) .AddVertexShader(shaders.vertScreenquad.get()) .AddFragmentShader(shaders.fragResolve.get()) .Topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST) .AddDynamicState(VK_DYNAMIC_STATE_VIEWPORT) .Viewport(0.0f, 0.0f, 0.0f, 0.0f) .Scissor(0, 0, 4096, 4096) .DebugName("resolve.pipeline") .Create(fb->GetDevice()); resolve.descriptorPool = DescriptorPoolBuilder() .AddPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1) .MaxSets(1) .DebugName("resolve.descriptorPool") .Create(fb->GetDevice()); resolve.sampler = SamplerBuilder() .DebugName("resolve.Sampler") .Create(fb->GetDevice()); } void VkLightmapper::CreateBlurPipeline() { blur.descriptorSetLayout = DescriptorSetLayoutBuilder() .AddBinding(0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT) .DebugName("blur.descriptorSetLayout") .Create(fb->GetDevice()); blur.pipelineLayout = PipelineLayoutBuilder() .AddSetLayout(blur.descriptorSetLayout.get()) .DebugName("blur.pipelineLayout") .Create(fb->GetDevice()); blur.renderPass = RenderPassBuilder() .AddAttachment( VK_FORMAT_R16G16B16A16_SFLOAT, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_STORE, VK_IMAGE_LAYOUT_UNDEFINED, 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("blur.renderpass") .Create(fb->GetDevice()); for (int i = 0; i < 2; i++) { blur.pipeline[i] = GraphicsPipelineBuilder() .Layout(blur.pipelineLayout.get()) .RenderPass(blur.renderPass.get()) .AddVertexShader(shaders.vertScreenquad.get()) .AddFragmentShader(shaders.fragBlur[i].get()) .Topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST) .AddDynamicState(VK_DYNAMIC_STATE_VIEWPORT) .Viewport(0.0f, 0.0f, 0.0f, 0.0f) .Scissor(0, 0, 4096, 4096) .DebugName("blur.pipeline") .Create(fb->GetDevice()); } blur.descriptorPool = DescriptorPoolBuilder() .AddPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2) .MaxSets(2) .DebugName("blur.descriptorPool") .Create(fb->GetDevice()); blur.sampler = SamplerBuilder() .MinFilter(VK_FILTER_NEAREST) .MagFilter(VK_FILTER_NEAREST) .MipmapMode(VK_SAMPLER_MIPMAP_MODE_NEAREST) .DebugName("blur.Sampler") .Create(fb->GetDevice()); } void VkLightmapper::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, 1) .AddPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1) .MaxSets(1) .DebugName("copy.descriptorPool") .Create(fb->GetDevice()); copy.sampler = SamplerBuilder() .DebugName("copy.Sampler") .Create(fb->GetDevice()); } void VkLightmapper::CreateBakeImage() { int width = bakeImageSize; int height = bakeImageSize; bakeImage.raytrace.Image = ImageBuilder() .Usage(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT) .Format(VK_FORMAT_R16G16B16A16_SFLOAT) .Size(width, height) .Samples(VK_SAMPLE_COUNT_4_BIT) .DebugName("LightmapImage.raytrace.Image") .Create(fb->GetDevice()); bakeImage.raytrace.View = ImageViewBuilder() .Image(bakeImage.raytrace.Image.get(), VK_FORMAT_R16G16B16A16_SFLOAT) .DebugName("LightmapImage.raytrace.View") .Create(fb->GetDevice()); bakeImage.raytrace.Framebuffer = FramebufferBuilder() .RenderPass(raytrace.renderPass.get()) .Size(width, height) .AddAttachment(bakeImage.raytrace.View.get()) .DebugName("LightmapImage.raytrace.Framebuffer") .Create(fb->GetDevice()); 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()); bakeImage.resolve.View = ImageViewBuilder() .Image(bakeImage.resolve.Image.get(), VK_FORMAT_R16G16B16A16_SFLOAT) .DebugName("LightmapImage.resolve.View") .Create(fb->GetDevice()); bakeImage.resolve.Framebuffer = FramebufferBuilder() .RenderPass(resolve.renderPass.get()) .Size(width, height) .AddAttachment(bakeImage.resolve.View.get()) .DebugName("LightmapImage.resolve.Framebuffer") .Create(fb->GetDevice()); bakeImage.resolve.DescriptorSet = resolve.descriptorPool->allocate(resolve.descriptorSetLayout.get()); bakeImage.resolve.DescriptorSet->SetDebugName("resolve.descriptorSet"); 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()); bakeImage.blur.View = ImageViewBuilder() .Image(bakeImage.blur.Image.get(), VK_FORMAT_R16G16B16A16_SFLOAT) .DebugName("LightmapImage.blur.View") .Create(fb->GetDevice()); bakeImage.blur.Framebuffer = FramebufferBuilder() .RenderPass(blur.renderPass.get()) .Size(width, height) .AddAttachment(bakeImage.blur.View.get()) .DebugName("LightmapImage.blur.Framebuffer") .Create(fb->GetDevice()); bakeImage.blur.DescriptorSet[0] = blur.descriptorPool->allocate(blur.descriptorSetLayout.get()); bakeImage.blur.DescriptorSet[0]->SetDebugName("blur.descriptorSet"); 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()); } void VkLightmapper::CreateUniformBuffer() { VkDeviceSize align = fb->GetDevice()->PhysicalDevice.Properties.Properties.limits.minUniformBufferOffsetAlignment; uniforms.StructStride = (sizeof(Uniforms) + align - 1) / align * align; uniforms.Buffer = BufferBuilder() .Usage(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT) .Size(uniforms.NumStructs * uniforms.StructStride) .DebugName("LightmapUniformBuffer") .Create(fb->GetDevice()); uniforms.TransferBuffer = BufferBuilder() .Usage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU) .Size(uniforms.NumStructs * uniforms.StructStride) .DebugName("LightmapUniformTransferBuffer") .Create(fb->GetDevice()); } void VkLightmapper::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); } void VkLightmapper::CreateDrawIndexedBuffer() { size_t size1 = sizeof(VkDrawIndexedIndirectCommand) * drawindexed.BufferSize; size_t size2 = sizeof(LightmapRaytracePC) * drawindexed.BufferSize; drawindexed.CommandsBuffer = BufferBuilder() .Usage( VK_BUFFER_USAGE_INDIRECT_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(size1) .DebugName("DrawIndexed.CommandsBuffer") .Create(fb->GetDevice()); drawindexed.ConstantsBuffer = 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(size2) .DebugName("DrawIndexed.ConstantsBuffer") .Create(fb->GetDevice()); drawindexed.Commands = (VkDrawIndexedIndirectCommand*)drawindexed.CommandsBuffer->Map(0, size1); drawindexed.Constants = (LightmapRaytracePC*)drawindexed.ConstantsBuffer->Map(0, size2); }