From 7f77dc3d70537468857d18b25cc3accebd4ac63d Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Thu, 10 Jul 2025 01:03:30 +0200 Subject: [PATCH] Bake probes on the GPU without downloading them to system memory Only apply probes after a full pass has completed Stop baking after 5 iterations --- .../hwrenderer/data/hw_lightprobe.cpp | 37 +-- .../rendering/hwrenderer/data/hw_lightprobe.h | 29 +- src/common/rendering/v_video.h | 7 +- .../rendering/vulkan/textures/vk_texture.cpp | 284 ++++++++++++++++-- .../rendering/vulkan/textures/vk_texture.h | 8 + .../rendering/vulkan/vk_lightprober.cpp | 130 ++++---- src/common/rendering/vulkan/vk_lightprober.h | 7 +- .../rendering/vulkan/vk_renderdevice.cpp | 30 +- src/common/rendering/vulkan/vk_renderdevice.h | 9 +- src/rendering/hwrenderer/hw_entrypoint.cpp | 18 +- 10 files changed, 394 insertions(+), 165 deletions(-) diff --git a/src/common/rendering/hwrenderer/data/hw_lightprobe.cpp b/src/common/rendering/hwrenderer/data/hw_lightprobe.cpp index ee8a56db2..71a0fe87c 100644 --- a/src/common/rendering/hwrenderer/data/hw_lightprobe.cpp +++ b/src/common/rendering/hwrenderer/data/hw_lightprobe.cpp @@ -1,15 +1,14 @@ #include "hw_lightprobe.h" +#include "v_video.h" -void LightProbeIncrementalBuilder::Step(const TArray& probes, std::function&, TArrayView&)> renderScene, std::function&, const TArray&)> uploadEnv) +void LightProbeIncrementalBuilder::Step(const TArray& probes, std::function renderScene) { + if (probes.size() == 0) + return; + if (lastIndex >= probes.size()) { lastIndex = 0; - - if (!probes.size()) - { - return; - } } if (cubemapsAllocated != probes.size()) @@ -17,39 +16,35 @@ void LightProbeIncrementalBuilder::Step(const TArray& probes, std::f int newSegments = probes.size(); int lastSegments = cubemapsAllocated; - irradianceMaps.resize(probes.size() * irradianceBytes); - prefilterMaps.resize(probes.size() * prefilterBytes); cubemapsAllocated = probes.size(); lastIndex = 0; collected = 0; + iterations = 0; - // needed because otherwise it somehow gets corrupted - memset(irradianceMaps.Data(), 0, irradianceMaps.Size() * sizeof(uint16_t)); - memset(prefilterMaps.Data(), 0, prefilterMaps.Size() * sizeof(uint16_t)); - - // workaround for lack of boundary checking in GPU - uploadEnv(this->irradianceMaps, this->prefilterMaps); + screen->ResetLightProbes(); return; } - auto irradianceBuffer = TArrayView(irradianceMaps.data() + lastIndex * irradianceBytes, irradianceBytes); - auto prefilterBuffer = TArrayView(prefilterMaps.data() + lastIndex * prefilterBytes, prefilterBytes); + if (iterations >= 5) + return; // We are done baking - renderScene(probes[lastIndex++], irradianceBuffer, prefilterBuffer); - ++collected; + renderScene(lastIndex, probes[lastIndex]); + lastIndex++; + collected++; if (lastIndex >= probes.size()) { if (collected == probes.size()) { - uploadEnv(this->irradianceMaps, this->prefilterMaps); + screen->EndLightProbePass(); } collected = 0; + iterations++; } } -void LightProbeIncrementalBuilder::Full(const TArray& probes, std::function&, TArrayView&)> renderScene, std::function&, const TArray&)> uploadEnv) +void LightProbeIncrementalBuilder::Full(const TArray& probes, std::function renderScene) { if (lastIndex >= probes.size()) { @@ -63,6 +58,6 @@ void LightProbeIncrementalBuilder::Full(const TArray& probes, std::f while (lastIndex < probes.size()) { - Step(probes, renderScene, uploadEnv); + Step(probes, renderScene); } } diff --git a/src/common/rendering/hwrenderer/data/hw_lightprobe.h b/src/common/rendering/hwrenderer/data/hw_lightprobe.h index 7f7b06002..66886581d 100644 --- a/src/common/rendering/hwrenderer/data/hw_lightprobe.h +++ b/src/common/rendering/hwrenderer/data/hw_lightprobe.h @@ -15,26 +15,15 @@ struct LightProbeTarget class LightProbeIncrementalBuilder { - int lastIndex = 0; - int collected = 0; - - int cubemapsAllocated = 0; - - TArray irradianceMaps; - TArray prefilterMaps; - - size_t irradianceBytes; - size_t prefilterBytes; public: - LightProbeIncrementalBuilder(size_t irradianceTexels, size_t prefilterTexels, size_t irradianceChannels, size_t prefilterChannels) : - irradianceBytes(irradianceTexels * irradianceChannels), - prefilterBytes(prefilterTexels * prefilterChannels) - { - } - - void Step(const TArray& probes, std::function&, TArrayView&)> renderScene, std::function&, const TArray&)> uploadEnv); - void Full(const TArray& probes, std::function&, TArrayView&)> renderScene, std::function&, const TArray&)> uploadEnv); + void Step(const TArray& probes, std::function renderScene); + void Full(const TArray& probes, std::function renderScene); auto GetStep() const { return lastIndex; } - auto GetBufferSize() const { return irradianceMaps.size() && prefilterMaps.size(); } -}; \ No newline at end of file + +private: + int lastIndex = 0; + int collected = 0; + int cubemapsAllocated = 0; + int iterations = 0; +}; diff --git a/src/common/rendering/v_video.h b/src/common/rendering/v_video.h index 68bb8fa35..54d2a84fc 100644 --- a/src/common/rendering/v_video.h +++ b/src/common/rendering/v_video.h @@ -229,8 +229,11 @@ public: // Get the array index for the material in the textures array accessible from shaders virtual int GetBindlessTextureIndex(FMaterial* material, int clampmode, int translation, bool paletteMode) { return -1; } - virtual void RenderEnvironmentMap(std::function renderFunc, TArrayView& irradianceMap, TArrayView& prefilterMap) {} - virtual void UploadEnvironmentMaps(int cubemapCount, const TArray& irradianceMaps, const TArray& prefilterMaps) {} + virtual void ResetLightProbes() {} + virtual void RenderLightProbe(int probeIndex, std::function renderFunc) {} + virtual void EndLightProbePass() {} + virtual void DownloadLightProbes(int probeCount, TArrayView irradianceMaps, TArrayView prefilterMaps) {} + virtual void UploadLightProbes(int probeCount, const TArray& irradianceMaps, const TArray& prefilterMaps) {} // Screen wiping virtual FTexture *WipeStartScreen(); diff --git a/src/common/rendering/vulkan/textures/vk_texture.cpp b/src/common/rendering/vulkan/textures/vk_texture.cpp index 1606ea5dc..82f35b5f3 100644 --- a/src/common/rendering/vulkan/textures/vk_texture.cpp +++ b/src/common/rendering/vulkan/textures/vk_texture.cpp @@ -414,20 +414,138 @@ void VkTextureManager::CreatePrefiltermap() UploadPrefiltermap(1, std::move(data)); } -void VkTextureManager::UploadIrradiancemap(int cubeCount, const TArray& srcPixels) +void VkTextureManager::ResetLightProbes() +{ + // Special thanks to Khronos for making it so simple to clear an image... + + auto cmdbuffer = fb->GetCommands()->GetTransferCommands(); + int miplevels = MAX_REFLECTION_LOD + 1; + + VkImageTransition barrier0; + for (auto& map : Prefiltermaps) + { + if (map.Image) + barrier0.AddImage(&map, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, true, 0, miplevels, 0, 6); + } + for (auto& map : Irradiancemaps) + { + if (map.Image) + barrier0.AddImage(&map, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, true, 0, 1, 0, 6); + } + barrier0.Execute(cmdbuffer); + + VkClearColorValue color = {}; + VkImageSubresourceRange range = {}; + range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + range.layerCount = 6; + range.levelCount = miplevels; + for (auto& map : Prefiltermaps) + { + if (map.Image) + cmdbuffer->clearColorImage(map.Image->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &color, 1, &range); + } + + range.levelCount = 1; + for (auto& map : Irradiancemaps) + { + if (map.Image) + cmdbuffer->clearColorImage(map.Image->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &color, 1, &range); + } + + VkImageTransition barrier1; + for (auto& map : Prefiltermaps) + { + if (map.Image) + barrier1.AddImage(&map, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, false, 0, miplevels, 0, 6); + } + for (auto& map : Irradiancemaps) + { + if (map.Image) + barrier1.AddImage(&map, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, false, 0, 1, 0, 6); + } + barrier1.Execute(cmdbuffer); +} + +void VkTextureManager::CopyIrradiancemap(const std::vector>& probes) +{ + CheckIrradiancemapSize(probes.size()); + + auto cmdbuffer = fb->GetCommands()->GetDrawCommands(); + + VkImageTransition barrier0; + for (size_t i = 0; i < probes.size(); i++) + barrier0.AddImage(&Irradiancemaps[i], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, true, 0, 1, 0, 6); + barrier0.Execute(cmdbuffer); + + VkImageCopy region = {}; + region.extent.width = IrradiancemapSize; + region.extent.height = IrradiancemapSize; + region.extent.depth = 1; + region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.srcSubresource.layerCount = 6; + region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.dstSubresource.layerCount = 6; + + for (size_t i = 0; i < probes.size(); i++) + { + cmdbuffer->copyImage(probes[i]->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, Irradiancemaps[i].Image->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + } + + VkImageTransition barrier1; + for (size_t i = 0; i < probes.size(); i++) + barrier1.AddImage(&Irradiancemaps[i], VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, false, 0, 1, 0, 6); + barrier1.Execute(cmdbuffer); +} + +void VkTextureManager::CopyPrefiltermap(const std::vector>& probes) +{ + CheckPrefiltermapSize(probes.size()); + + auto cmdbuffer = fb->GetCommands()->GetDrawCommands(); + int miplevels = MAX_REFLECTION_LOD + 1; + + VkImageTransition barrier0; + for (size_t i = 0; i < probes.size(); i++) + barrier0.AddImage(&Prefiltermaps[i], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, true, 0, miplevels, 0, 6); + barrier0.Execute(cmdbuffer); + + std::vector regions; + for (int level = 0; level < miplevels; level++) + { + VkImageCopy region = {}; + region.extent.width = PrefiltermapSize >> level; + region.extent.height = PrefiltermapSize >> level; + region.extent.depth = 1; + region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.srcSubresource.layerCount = 6; + region.srcSubresource.mipLevel = level; + region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.dstSubresource.layerCount = 6; + region.dstSubresource.mipLevel = level; + regions.push_back(region); + } + + for (size_t i = 0; i < probes.size(); i++) + { + cmdbuffer->copyImage(probes[i]->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, Prefiltermaps[i].Image->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, (uint32_t)regions.size(), regions.data()); + } + + VkImageTransition barrier1; + for (size_t i = 0; i < probes.size(); i++) + barrier1.AddImage(&Prefiltermaps[i], VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, false, 0, miplevels, 0, 6); + barrier1.Execute(cmdbuffer); +} + +void VkTextureManager::CheckIrradiancemapSize(int cubeCount) { int createStart = Irradiancemaps.size(); if (Irradiancemaps.size() <= (size_t)cubeCount) Irradiancemaps.resize(cubeCount); - int w = IrradiancemapSize; - int h = IrradiancemapSize; - int pixelsize = 8; - for (int i = createStart; i < cubeCount; i++) { Irradiancemaps[i].Image = ImageBuilder() - .Size(w, h, 1, 6) + .Size(IrradiancemapSize, IrradiancemapSize, 1, 6) .Format(VK_FORMAT_R16G16B16A16_SFLOAT) .Usage(VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT) .Flags(VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT) @@ -440,6 +558,44 @@ void VkTextureManager::UploadIrradiancemap(int cubeCount, const TArray .DebugName("VkTextureManager.IrradiancemapView") .Create(fb->GetDevice()); } +} + +void VkTextureManager::CheckPrefiltermapSize(int cubeCount) +{ + int createStart = Prefiltermaps.size(); + if (Prefiltermaps.size() <= (size_t)cubeCount) + Prefiltermaps.resize(cubeCount); + + int w = PrefiltermapSize; + int h = PrefiltermapSize; + int pixelsize = 8; + int miplevels = MAX_REFLECTION_LOD + 1; + + for (int i = createStart; i < cubeCount; i++) + { + Prefiltermaps[i].Image = ImageBuilder() + .Size(w, h, miplevels, 6) + .Format(VK_FORMAT_R16G16B16A16_SFLOAT) + .Usage(VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT) + .Flags(VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT) + .DebugName("VkTextureManager.Prefiltermap") + .Create(fb->GetDevice()); + + Prefiltermaps[i].View = ImageViewBuilder() + .Type(VK_IMAGE_VIEW_TYPE_CUBE) + .Image(Prefiltermaps[i].Image.get(), VK_FORMAT_R16G16B16A16_SFLOAT) + .DebugName("VkTextureManager.PrefiltermapView") + .Create(fb->GetDevice()); + } +} + +void VkTextureManager::UploadIrradiancemap(int cubeCount, const TArray& srcPixels) +{ + CheckIrradiancemapSize(cubeCount); + + int w = IrradiancemapSize; + int h = IrradiancemapSize; + int pixelsize = 8; auto cmdbuffer = fb->GetCommands()->GetTransferCommands(); @@ -495,32 +651,13 @@ void VkTextureManager::UploadIrradiancemap(int cubeCount, const TArray void VkTextureManager::UploadPrefiltermap(int cubeCount, const TArray& srcPixels) { - int createStart = Prefiltermaps.size(); - if (Prefiltermaps.size() <= (size_t)cubeCount) - Prefiltermaps.resize(cubeCount); + CheckPrefiltermapSize(cubeCount); int w = PrefiltermapSize; int h = PrefiltermapSize; int pixelsize = 8; int miplevels = MAX_REFLECTION_LOD + 1; - for (int i = createStart; i < cubeCount; i++) - { - Prefiltermaps[i].Image = ImageBuilder() - .Size(w, h, miplevels, 6) - .Format(VK_FORMAT_R16G16B16A16_SFLOAT) - .Usage(VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT) - .Flags(VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT) - .DebugName("VkTextureManager.Prefiltermap") - .Create(fb->GetDevice()); - - Prefiltermaps[i].View = ImageViewBuilder() - .Type(VK_IMAGE_VIEW_TYPE_CUBE) - .Image(Prefiltermaps[i].Image.get(), VK_FORMAT_R16G16B16A16_SFLOAT) - .DebugName("VkTextureManager.PrefiltermapView") - .Create(fb->GetDevice()); - } - auto cmdbuffer = fb->GetCommands()->GetTransferCommands(); if (srcPixels.size() != 0) @@ -594,6 +731,101 @@ void VkTextureManager::UploadPrefiltermap(int cubeCount, const TArray& barrier1.Execute(cmdbuffer); } +void VkTextureManager::DownloadIrradiancemap(int probeCount, TArrayView databuffer) +{ +#if 0 + const int texelCount = DFrameBuffer::irrandiaceMapTexelCount; + + if (databuffer.Size() < texelCount) + return; + + auto staging = BufferBuilder() + .Size(32 * 32 * 8 * 6) + .Usage(VK_BUFFER_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_GPU_TO_CPU) + .Create(fb->GetDevice()); + + PipelineBarrier barrier1; + for (int i = 0; i < 6; i++) + barrier1.AddImage(irradianceMap.images[i].get(), VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT); + barrier1.Execute(cmdbuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); + + for (int i = 0; i < 6; i++) + { + VkBufferImageCopy region = { }; + region.bufferOffset = 32 * 32 * 8 * i; + region.imageExtent.width = irradianceMap.images[i]->width; + region.imageExtent.height = irradianceMap.images[i]->height; + region.imageExtent.depth = 1; + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.layerCount = 1; + cmdbuffer->copyImageToBuffer(irradianceMap.images[i]->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, staging->buffer, 1, ®ion); + } + + fb->GetCommands()->WaitForCommands(false); + + // Copy while dropping the alpha channel + auto dst = databuffer.Data(); + auto src = (uint16_t*)staging->Map(0, texelCount * 8); + for (int i = 0; i < texelCount; i++) + { + *(dst++) = *(src++); + *(dst++) = *(src++); + *(dst++) = *(src++); + src++; + } + staging->Unmap(); +#endif +} + +void VkTextureManager::DownloadPrefiltermap(int probeCount, TArrayView databuffer) +{ +#if 0 + const int texelCount = DFrameBuffer::prefilterMapTexelCount; + + if (databuffer.Size() < texelCount) + return; + + auto staging = BufferBuilder() + .Size(prefilterMap.levelsSize * 6 * 8) + .Usage(VK_BUFFER_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_GPU_TO_CPU) + .Create(fb->GetDevice()); + + PipelineBarrier barrier1; + for (int i = 0; i < 6 * prefilterMap.maxlevels; i++) + barrier1.AddImage(prefilterMap.images[i].get(), VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT); + barrier1.Execute(cmdbuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); + + int offset = 0; + for (int i = 0; i < 6 * prefilterMap.maxlevels; i++) + { + VkBufferImageCopy region = { }; + region.bufferOffset = offset; + region.imageExtent.width = prefilterMap.images[i]->width; + region.imageExtent.height = prefilterMap.images[i]->height; + region.imageExtent.depth = 1; + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.layerCount = 1; + cmdbuffer->copyImageToBuffer(prefilterMap.images[i]->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, staging->buffer, 1, ®ion); + + offset += prefilterMap.images[i]->width * prefilterMap.images[i]->height * 8; + } + + fb->GetCommands()->WaitForCommands(false); + + // Copy while dropping the alpha channel + auto dst = databuffer.Data(); + auto src = (uint16_t*)staging->Map(0, texelCount * 8); + for (int i = 0; i < texelCount; i++) + { + *(dst++) = *(src++); + *(dst++) = *(src++); + *(dst++) = *(src++); + src++; + } + staging->Unmap(); +#endif +} + void VkTextureManager::SetLightmapCount(int size, int count) { int startIndex = Lightmaps.size(); diff --git a/src/common/rendering/vulkan/textures/vk_texture.h b/src/common/rendering/vulkan/textures/vk_texture.h index 7b2bbf8e5..4201451b5 100644 --- a/src/common/rendering/vulkan/textures/vk_texture.h +++ b/src/common/rendering/vulkan/textures/vk_texture.h @@ -33,8 +33,16 @@ public: void CreateLightmap(int size, int count, const TArray& data); void DownloadLightmap(int arrayIndex, uint16_t* buffer); + void ResetLightProbes(); void UploadIrradiancemap(int cubeCount, const TArray& data); void UploadPrefiltermap(int cubeCount, const TArray& data); + void CopyIrradiancemap(const std::vector>& probes); + void CopyPrefiltermap(const std::vector>& probes); + void DownloadIrradiancemap(int probeCount, TArrayView irradianceMaps); + void DownloadPrefiltermap(int probeCount, TArrayView prefilterMaps); + + void CheckIrradiancemapSize(int cubeCount); + void CheckPrefiltermapSize(int cubeCount); void SetGamePalette(); diff --git a/src/common/rendering/vulkan/vk_lightprober.cpp b/src/common/rendering/vulkan/vk_lightprober.cpp index c3c4e8ab9..eafeecafe 100644 --- a/src/common/rendering/vulkan/vk_lightprober.cpp +++ b/src/common/rendering/vulkan/vk_lightprober.cpp @@ -252,20 +252,8 @@ void VkLightprober::CreateIrradianceMap() } } -bool VkLightprober::GenerateIrradianceMap(TArrayView& databuffer) +void VkLightprober::GenerateIrradianceMap(int probeIndex) { - const int texelCount = DFrameBuffer::irrandiaceMapTexelCount; - - if (databuffer.Size() < texelCount) - { - return false; - } - - auto staging = BufferBuilder() - .Size(32 * 32 * 8 * 6) - .Usage(VK_BUFFER_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_GPU_TO_CPU) - .Create(fb->GetDevice()); - WriteDescriptors write; for (int i = 0; i < 6; i++) { @@ -317,37 +305,40 @@ bool VkLightprober::GenerateIrradianceMap(TArrayView& databuffer) cmdbuffer->dispatch(32, 32, 1); } + if (irradianceMap.probes.size() <= (size_t)probeIndex) + irradianceMap.probes.resize(probeIndex + 1); + if (!irradianceMap.probes[probeIndex]) + { + irradianceMap.probes[probeIndex] = ImageBuilder() + .Size(32, 32, 1, 6) + .Format(VK_FORMAT_R16G16B16A16_SFLOAT) + .Usage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT) + .Create(fb->GetDevice()); + } + PipelineBarrier barrier1; for (int i = 0; i < 6; i++) barrier1.AddImage(irradianceMap.images[i].get(), VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT); + barrier1.AddImage(irradianceMap.probes[probeIndex].get(), VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 6); barrier1.Execute(cmdbuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); for (int i = 0; i < 6; i++) { - VkBufferImageCopy region = { }; - region.bufferOffset = 32 * 32 * 8 * i; - region.imageExtent.width = irradianceMap.images[i]->width; - region.imageExtent.height = irradianceMap.images[i]->height; - region.imageExtent.depth = 1; - region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - region.imageSubresource.layerCount = 1; - cmdbuffer->copyImageToBuffer(irradianceMap.images[i]->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, staging->buffer, 1, ®ion); + VkImageCopy region = {}; + region.extent.width = irradianceMap.images[i]->width; + region.extent.height = irradianceMap.images[i]->height; + region.extent.depth = 1; + region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.srcSubresource.layerCount = 1; + region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.dstSubresource.layerCount = 1; + region.dstSubresource.baseArrayLayer = i; + cmdbuffer->copyImage(irradianceMap.images[i]->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, irradianceMap.probes[probeIndex]->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); } - fb->GetCommands()->WaitForCommands(false); - - // Copy while dropping the alpha channel - auto dst = databuffer.Data(); - auto src = (uint16_t*)staging->Map(0, texelCount * 8); - for (int i = 0; i < texelCount; i++) - { - *(dst++) = *(src++); - *(dst++) = *(src++); - *(dst++) = *(src++); - src++; - } - staging->Unmap(); - return true; + PipelineBarrier barrier2; + barrier2.AddImage(irradianceMap.probes[probeIndex].get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 6); + barrier2.Execute(cmdbuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); } void VkLightprober::CreatePrefilterMap() @@ -397,20 +388,8 @@ void VkLightprober::CreatePrefilterMap() } } -bool VkLightprober::GeneratePrefilterMap(TArrayView& databuffer) +void VkLightprober::GeneratePrefilterMap(int probeIndex) { - const int texelCount = DFrameBuffer::prefilterMapTexelCount; - - if (databuffer.Size() < texelCount) - { - return false; - } - - auto staging = BufferBuilder() - .Size(prefilterMap.levelsSize * 6 * 8) - .Usage(VK_BUFFER_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_GPU_TO_CPU) - .Create(fb->GetDevice()); - WriteDescriptors write; for (int i = 0; i < 6 * prefilterMap.maxlevels; i++) { @@ -467,40 +446,47 @@ bool VkLightprober::GeneratePrefilterMap(TArrayView& databuffer) } } + if (prefilterMap.probes.size() <= (size_t)probeIndex) + prefilterMap.probes.resize(probeIndex + 1); + if (!prefilterMap.probes[probeIndex]) + { + prefilterMap.probes[probeIndex] = ImageBuilder() + .Size(128, 128, prefilterMap.maxlevels, 6) + .Format(VK_FORMAT_R16G16B16A16_SFLOAT) + .Usage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT) + .Create(fb->GetDevice()); + } + PipelineBarrier barrier1; for (int i = 0; i < 6 * prefilterMap.maxlevels; i++) barrier1.AddImage(prefilterMap.images[i].get(), VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT); + barrier1.AddImage(prefilterMap.probes[probeIndex].get(), VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_ASPECT_COLOR_BIT, 0, prefilterMap.maxlevels, 0, 6); barrier1.Execute(cmdbuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); - int offset = 0; for (int i = 0; i < 6 * prefilterMap.maxlevels; i++) { - VkBufferImageCopy region = { }; - region.bufferOffset = offset; - region.imageExtent.width = prefilterMap.images[i]->width; - region.imageExtent.height = prefilterMap.images[i]->height; - region.imageExtent.depth = 1; - region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - region.imageSubresource.layerCount = 1; - cmdbuffer->copyImageToBuffer(prefilterMap.images[i]->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, staging->buffer, 1, ®ion); - - offset += prefilterMap.images[i]->width * prefilterMap.images[i]->height * 8; + VkImageCopy region = {}; + region.extent.width = prefilterMap.images[i]->width; + region.extent.height = prefilterMap.images[i]->height; + region.extent.depth = 1; + region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.srcSubresource.layerCount = 1; + region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.dstSubresource.layerCount = 1; + region.dstSubresource.baseArrayLayer = i / prefilterMap.maxlevels; + region.dstSubresource.mipLevel = i % prefilterMap.maxlevels; + cmdbuffer->copyImage(prefilterMap.images[i]->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, prefilterMap.probes[probeIndex]->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); } - fb->GetCommands()->WaitForCommands(false); + PipelineBarrier barrier2; + barrier2.AddImage(prefilterMap.probes[probeIndex].get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_ASPECT_COLOR_BIT, 0, prefilterMap.maxlevels, 0, 6); + barrier2.Execute(cmdbuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); +} - // Copy while dropping the alpha channel - auto dst = databuffer.Data(); - auto src = (uint16_t*)staging->Map(0, texelCount * 8); - for (int i = 0; i < texelCount; i++) - { - *(dst++) = *(src++); - *(dst++) = *(src++); - *(dst++) = *(src++); - src++; - } - staging->Unmap(); - return true; +void VkLightprober::EndLightProbePass() +{ + fb->GetTextureManager()->CopyIrradiancemap(irradianceMap.probes); + fb->GetTextureManager()->CopyPrefiltermap(prefilterMap.probes); } std::vector VkLightprober::CompileShader(const std::string& filename) diff --git a/src/common/rendering/vulkan/vk_lightprober.h b/src/common/rendering/vulkan/vk_lightprober.h index eb3abbdb4..b666f554d 100644 --- a/src/common/rendering/vulkan/vk_lightprober.h +++ b/src/common/rendering/vulkan/vk_lightprober.h @@ -35,8 +35,9 @@ public: ~VkLightprober(); void RenderEnvironmentMap(std::function renderFunc); - bool GenerateIrradianceMap(TArrayView& databuffer); - bool GeneratePrefilterMap(TArrayView& databuffer); + void GenerateIrradianceMap(int probeIndex); + void GeneratePrefilterMap(int probeIndex); + void EndLightProbePass(); private: void CreateBrdfLutResources(); @@ -85,6 +86,7 @@ private: std::unique_ptr sampler; std::unique_ptr images[6]; std::unique_ptr views[6]; + std::vector> probes; } irradianceMap; struct PrefilterMap @@ -102,6 +104,7 @@ private: std::unique_ptr sampler; std::unique_ptr images[6 * maxlevels]; std::unique_ptr views[6 * maxlevels]; + std::vector> probes; } prefilterMap; VulkanRenderDevice* fb = nullptr; diff --git a/src/common/rendering/vulkan/vk_renderdevice.cpp b/src/common/rendering/vulkan/vk_renderdevice.cpp index 534ae599e..6d2b7a691 100644 --- a/src/common/rendering/vulkan/vk_renderdevice.cpp +++ b/src/common/rendering/vulkan/vk_renderdevice.cpp @@ -406,17 +406,33 @@ void VulkanRenderDevice::RenderTextureView(FCanvasTexture* tex, std::functionSetUpdated(true); } -void VulkanRenderDevice::RenderEnvironmentMap(std::function renderFunc, TArrayView& irradianceMap, TArrayView& prefilterMap) +void VulkanRenderDevice::ResetLightProbes() { - mLightprober->RenderEnvironmentMap(std::move(renderFunc)); - mLightprober->GenerateIrradianceMap(irradianceMap); - mLightprober->GeneratePrefilterMap(prefilterMap); + mTextureManager->ResetLightProbes(); } -void VulkanRenderDevice::UploadEnvironmentMaps(int cubemapCount, const TArray& irradianceMaps, const TArray& prefilterMaps) +void VulkanRenderDevice::RenderLightProbe(int probeIndex, std::function renderFunc) { - mTextureManager->UploadIrradiancemap(cubemapCount, irradianceMaps); - mTextureManager->UploadPrefiltermap(cubemapCount, prefilterMaps); + mLightprober->RenderEnvironmentMap(std::move(renderFunc)); + mLightprober->GenerateIrradianceMap(probeIndex); + mLightprober->GeneratePrefilterMap(probeIndex); +} + +void VulkanRenderDevice::EndLightProbePass() +{ + mLightprober->EndLightProbePass(); +} + +void VulkanRenderDevice::DownloadLightProbes(int probeCount, TArrayView irradianceMaps, TArrayView prefilterMaps) +{ + mTextureManager->DownloadIrradiancemap(probeCount, irradianceMaps); + mTextureManager->DownloadPrefiltermap(probeCount, prefilterMaps); +} + +void VulkanRenderDevice::UploadLightProbes(int probeCount, const TArray& irradianceMaps, const TArray& prefilterMaps) +{ + mTextureManager->UploadIrradiancemap(probeCount, irradianceMaps); + mTextureManager->UploadPrefiltermap(probeCount, prefilterMaps); } void VulkanRenderDevice::PostProcessScene(bool swscene, int fixedcm, float flash, bool palettePostprocess, const std::function &afterBloomDrawEndScene2D) diff --git a/src/common/rendering/vulkan/vk_renderdevice.h b/src/common/rendering/vulkan/vk_renderdevice.h index c1b13922b..dac8cc8cb 100644 --- a/src/common/rendering/vulkan/vk_renderdevice.h +++ b/src/common/rendering/vulkan/vk_renderdevice.h @@ -109,8 +109,13 @@ public: private: void RenderTextureView(FCanvasTexture* tex, std::function renderFunc) override; - void RenderEnvironmentMap(std::function renderFunc, TArrayView& irradianceMap, TArrayView& prefilterMap) override; - void UploadEnvironmentMaps(int cubemapCount, const TArray& irradianceMaps, const TArray& prefilterMaps) override; + + void ResetLightProbes() override; + void RenderLightProbe(int probeIndex, std::function renderFunc) override; + void EndLightProbePass() override; + void DownloadLightProbes(int probeCount, TArrayView irradianceMaps, TArrayView prefilterMaps) override; + void UploadLightProbes(int probeCount, const TArray& irradianceMaps, const TArray& prefilterMaps) override; + void PrintStartupLog(); void CopyScreenToBuffer(int w, int h, uint8_t *data) override; diff --git a/src/rendering/hwrenderer/hw_entrypoint.cpp b/src/rendering/hwrenderer/hw_entrypoint.cpp index 1be1a4a8e..ba78b7183 100644 --- a/src/rendering/hwrenderer/hw_entrypoint.cpp +++ b/src/rendering/hwrenderer/hw_entrypoint.cpp @@ -336,7 +336,7 @@ static void CheckTimer(FRenderState &state, uint64_t ShaderStartTime) state.firstFrame = screen->FrameTime - 1; } -LightProbeIncrementalBuilder lightProbeBuilder(DFrameBuffer::irrandiaceMapTexelCount, DFrameBuffer::prefilterMapTexelCount, DFrameBuffer::irradianceMapChannelCount, DFrameBuffer::prefilterMapChannelCount); +LightProbeIncrementalBuilder lightProbeBuilder; sector_t* RenderView(player_t* player) { @@ -413,30 +413,22 @@ sector_t* RenderView(player_t* player) AActor* lightprobe = level.GetThinkerIterator(NAME_LightProbe, STAT_INFO).Next(); if (lightprobe) { - auto renderEnvMap = [&](const LightProbe& probe, TArrayView& irradianceMap, TArrayView& prefilteredMap) { + auto renderEnvMap = [&](int probeIndex, const LightProbe& probe) { lightprobe->SetOrigin(DVector3(probe.position), false); // crime against nature // The renderer interpolates camera in its own mechanism that has to be disabled when moving around the single probe bool noInterpolate = r_NoInterpolate; r_NoInterpolate = true; - screen->RenderEnvironmentMap([&](IntRect& bounds, int side) { + screen->RenderLightProbe(probeIndex, [&](IntRect& bounds, int side) { FRenderViewpoint probevp; RenderViewpoint(probevp, lightprobe, &bounds, 90.0, 1.0f, 1.0f, false, false, side); - }, irradianceMap, prefilteredMap); + }); r_NoInterpolate = noInterpolate; }; - auto renderEnvScreen = [&](const TArray& irradianceMaps, const TArray& prefilteredMaps) { - screen->UploadEnvironmentMaps(level.lightProbes.size(), irradianceMaps, prefilteredMaps); - }; - - lightProbeBuilder.Step( - level.lightProbes, - renderEnvMap, - renderEnvScreen - ); + lightProbeBuilder.Step(level.lightProbes, renderEnvMap); } else {