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
This commit is contained in:
parent
50183606d2
commit
7f77dc3d70
10 changed files with 393 additions and 164 deletions
|
|
@ -1,15 +1,14 @@
|
|||
#include "hw_lightprobe.h"
|
||||
#include "v_video.h"
|
||||
|
||||
void LightProbeIncrementalBuilder::Step(const TArray<LightProbe>& probes, std::function<void(const LightProbe&, TArrayView<uint16_t>&, TArrayView<uint16_t>&)> renderScene, std::function<void(const TArray<uint16_t>&, const TArray<uint16_t>&)> uploadEnv)
|
||||
void LightProbeIncrementalBuilder::Step(const TArray<LightProbe>& probes, std::function<void(int probeIndex, const LightProbe& probe)> 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<LightProbe>& 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<uint16_t>(irradianceMaps.data() + lastIndex * irradianceBytes, irradianceBytes);
|
||||
auto prefilterBuffer = TArrayView<uint16_t>(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<LightProbe>& probes, std::function<void(const LightProbe&, TArrayView<uint16_t>&, TArrayView<uint16_t>&)> renderScene, std::function<void(const TArray<uint16_t>&, const TArray<uint16_t>&)> uploadEnv)
|
||||
void LightProbeIncrementalBuilder::Full(const TArray<LightProbe>& probes, std::function<void(int probeIndex, const LightProbe& probe)> renderScene)
|
||||
{
|
||||
if (lastIndex >= probes.size())
|
||||
{
|
||||
|
|
@ -63,6 +58,6 @@ void LightProbeIncrementalBuilder::Full(const TArray<LightProbe>& probes, std::f
|
|||
|
||||
while (lastIndex < probes.size())
|
||||
{
|
||||
Step(probes, renderScene, uploadEnv);
|
||||
Step(probes, renderScene);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,26 +15,15 @@ struct LightProbeTarget
|
|||
|
||||
class LightProbeIncrementalBuilder
|
||||
{
|
||||
int lastIndex = 0;
|
||||
int collected = 0;
|
||||
|
||||
int cubemapsAllocated = 0;
|
||||
|
||||
TArray<uint16_t> irradianceMaps;
|
||||
TArray<uint16_t> 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<LightProbe>& probes, std::function<void(const LightProbe&, TArrayView<uint16_t>&, TArrayView<uint16_t>&)> renderScene, std::function<void(const TArray<uint16_t>&, const TArray<uint16_t>&)> uploadEnv);
|
||||
void Full(const TArray<LightProbe>& probes, std::function<void(const LightProbe&, TArrayView<uint16_t>&, TArrayView<uint16_t>&)> renderScene, std::function<void(const TArray<uint16_t>&, const TArray<uint16_t>&)> uploadEnv);
|
||||
void Step(const TArray<LightProbe>& probes, std::function<void(int probeIndex, const LightProbe& probe)> renderScene);
|
||||
void Full(const TArray<LightProbe>& probes, std::function<void(int probeIndex, const LightProbe& probe)> renderScene);
|
||||
|
||||
auto GetStep() const { return lastIndex; }
|
||||
auto GetBufferSize() const { return irradianceMaps.size() && prefilterMaps.size(); }
|
||||
};
|
||||
|
||||
private:
|
||||
int lastIndex = 0;
|
||||
int collected = 0;
|
||||
int cubemapsAllocated = 0;
|
||||
int iterations = 0;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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<void(IntRect& bounds, int side)> renderFunc, TArrayView<uint16_t>& irradianceMap, TArrayView<uint16_t>& prefilterMap) {}
|
||||
virtual void UploadEnvironmentMaps(int cubemapCount, const TArray<uint16_t>& irradianceMaps, const TArray<uint16_t>& prefilterMaps) {}
|
||||
virtual void ResetLightProbes() {}
|
||||
virtual void RenderLightProbe(int probeIndex, std::function<void(IntRect& bounds, int side)> renderFunc) {}
|
||||
virtual void EndLightProbePass() {}
|
||||
virtual void DownloadLightProbes(int probeCount, TArrayView<uint16_t> irradianceMaps, TArrayView<uint16_t> prefilterMaps) {}
|
||||
virtual void UploadLightProbes(int probeCount, const TArray<uint16_t>& irradianceMaps, const TArray<uint16_t>& prefilterMaps) {}
|
||||
|
||||
// Screen wiping
|
||||
virtual FTexture *WipeStartScreen();
|
||||
|
|
|
|||
|
|
@ -414,20 +414,138 @@ void VkTextureManager::CreatePrefiltermap()
|
|||
UploadPrefiltermap(1, std::move(data));
|
||||
}
|
||||
|
||||
void VkTextureManager::UploadIrradiancemap(int cubeCount, const TArray<uint16_t>& 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<std::unique_ptr<VulkanImage>>& 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<std::unique_ptr<VulkanImage>>& 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<VkImageCopy> 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<uint16_t>
|
|||
.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<uint16_t>& 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<uint16_t>
|
|||
|
||||
void VkTextureManager::UploadPrefiltermap(int cubeCount, const TArray<uint16_t>& 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<uint16_t>&
|
|||
barrier1.Execute(cmdbuffer);
|
||||
}
|
||||
|
||||
void VkTextureManager::DownloadIrradiancemap(int probeCount, TArrayView<uint16_t> 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<uint16_t> 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();
|
||||
|
|
|
|||
|
|
@ -33,8 +33,16 @@ public:
|
|||
void CreateLightmap(int size, int count, const TArray<uint16_t>& data);
|
||||
void DownloadLightmap(int arrayIndex, uint16_t* buffer);
|
||||
|
||||
void ResetLightProbes();
|
||||
void UploadIrradiancemap(int cubeCount, const TArray<uint16_t>& data);
|
||||
void UploadPrefiltermap(int cubeCount, const TArray<uint16_t>& data);
|
||||
void CopyIrradiancemap(const std::vector<std::unique_ptr<VulkanImage>>& probes);
|
||||
void CopyPrefiltermap(const std::vector<std::unique_ptr<VulkanImage>>& probes);
|
||||
void DownloadIrradiancemap(int probeCount, TArrayView<uint16_t> irradianceMaps);
|
||||
void DownloadPrefiltermap(int probeCount, TArrayView<uint16_t> prefilterMaps);
|
||||
|
||||
void CheckIrradiancemapSize(int cubeCount);
|
||||
void CheckPrefiltermapSize(int cubeCount);
|
||||
|
||||
void SetGamePalette();
|
||||
|
||||
|
|
|
|||
|
|
@ -252,20 +252,8 @@ void VkLightprober::CreateIrradianceMap()
|
|||
}
|
||||
}
|
||||
|
||||
bool VkLightprober::GenerateIrradianceMap(TArrayView<uint16_t>& 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<uint16_t>& 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<uint16_t>& 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<uint16_t>& 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<uint32_t> VkLightprober::CompileShader(const std::string& filename)
|
||||
|
|
|
|||
|
|
@ -35,8 +35,9 @@ public:
|
|||
~VkLightprober();
|
||||
|
||||
void RenderEnvironmentMap(std::function<void(IntRect& bounds, int side)> renderFunc);
|
||||
bool GenerateIrradianceMap(TArrayView<uint16_t>& databuffer);
|
||||
bool GeneratePrefilterMap(TArrayView<uint16_t>& databuffer);
|
||||
void GenerateIrradianceMap(int probeIndex);
|
||||
void GeneratePrefilterMap(int probeIndex);
|
||||
void EndLightProbePass();
|
||||
|
||||
private:
|
||||
void CreateBrdfLutResources();
|
||||
|
|
@ -85,6 +86,7 @@ private:
|
|||
std::unique_ptr<VulkanSampler> sampler;
|
||||
std::unique_ptr<VulkanImage> images[6];
|
||||
std::unique_ptr<VulkanImageView> views[6];
|
||||
std::vector<std::unique_ptr<VulkanImage>> probes;
|
||||
} irradianceMap;
|
||||
|
||||
struct PrefilterMap
|
||||
|
|
@ -102,6 +104,7 @@ private:
|
|||
std::unique_ptr<VulkanSampler> sampler;
|
||||
std::unique_ptr<VulkanImage> images[6 * maxlevels];
|
||||
std::unique_ptr<VulkanImageView> views[6 * maxlevels];
|
||||
std::vector<std::unique_ptr<VulkanImage>> probes;
|
||||
} prefilterMap;
|
||||
|
||||
VulkanRenderDevice* fb = nullptr;
|
||||
|
|
|
|||
|
|
@ -406,17 +406,33 @@ void VulkanRenderDevice::RenderTextureView(FCanvasTexture* tex, std::function<vo
|
|||
tex->SetUpdated(true);
|
||||
}
|
||||
|
||||
void VulkanRenderDevice::RenderEnvironmentMap(std::function<void(IntRect& bounds, int side)> renderFunc, TArrayView<uint16_t>& irradianceMap, TArrayView<uint16_t>& prefilterMap)
|
||||
void VulkanRenderDevice::ResetLightProbes()
|
||||
{
|
||||
mLightprober->RenderEnvironmentMap(std::move(renderFunc));
|
||||
mLightprober->GenerateIrradianceMap(irradianceMap);
|
||||
mLightprober->GeneratePrefilterMap(prefilterMap);
|
||||
mTextureManager->ResetLightProbes();
|
||||
}
|
||||
|
||||
void VulkanRenderDevice::UploadEnvironmentMaps(int cubemapCount, const TArray<uint16_t>& irradianceMaps, const TArray<uint16_t>& prefilterMaps)
|
||||
void VulkanRenderDevice::RenderLightProbe(int probeIndex, std::function<void(IntRect& bounds, int side)> 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<uint16_t> irradianceMaps, TArrayView<uint16_t> prefilterMaps)
|
||||
{
|
||||
mTextureManager->DownloadIrradiancemap(probeCount, irradianceMaps);
|
||||
mTextureManager->DownloadPrefiltermap(probeCount, prefilterMaps);
|
||||
}
|
||||
|
||||
void VulkanRenderDevice::UploadLightProbes(int probeCount, const TArray<uint16_t>& irradianceMaps, const TArray<uint16_t>& prefilterMaps)
|
||||
{
|
||||
mTextureManager->UploadIrradiancemap(probeCount, irradianceMaps);
|
||||
mTextureManager->UploadPrefiltermap(probeCount, prefilterMaps);
|
||||
}
|
||||
|
||||
void VulkanRenderDevice::PostProcessScene(bool swscene, int fixedcm, float flash, bool palettePostprocess, const std::function<void()> &afterBloomDrawEndScene2D)
|
||||
|
|
|
|||
|
|
@ -109,8 +109,13 @@ public:
|
|||
|
||||
private:
|
||||
void RenderTextureView(FCanvasTexture* tex, std::function<void(IntRect &)> renderFunc) override;
|
||||
void RenderEnvironmentMap(std::function<void(IntRect& bounds, int side)> renderFunc, TArrayView<uint16_t>& irradianceMap, TArrayView<uint16_t>& prefilterMap) override;
|
||||
void UploadEnvironmentMaps(int cubemapCount, const TArray<uint16_t>& irradianceMaps, const TArray<uint16_t>& prefilterMaps) override;
|
||||
|
||||
void ResetLightProbes() override;
|
||||
void RenderLightProbe(int probeIndex, std::function<void(IntRect& bounds, int side)> renderFunc) override;
|
||||
void EndLightProbePass() override;
|
||||
void DownloadLightProbes(int probeCount, TArrayView<uint16_t> irradianceMaps, TArrayView<uint16_t> prefilterMaps) override;
|
||||
void UploadLightProbes(int probeCount, const TArray<uint16_t>& irradianceMaps, const TArray<uint16_t>& prefilterMaps) override;
|
||||
|
||||
void PrintStartupLog();
|
||||
void CopyScreenToBuffer(int w, int h, uint8_t *data) override;
|
||||
|
||||
|
|
|
|||
|
|
@ -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<AActor>(NAME_LightProbe, STAT_INFO).Next();
|
||||
if (lightprobe)
|
||||
{
|
||||
auto renderEnvMap = [&](const LightProbe& probe, TArrayView<uint16_t>& irradianceMap, TArrayView<uint16_t>& 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<uint16_t>& irradianceMaps, const TArray<uint16_t>& prefilteredMaps) {
|
||||
screen->UploadEnvironmentMaps(level.lightProbes.size(), irradianceMaps, prefilteredMaps);
|
||||
};
|
||||
|
||||
lightProbeBuilder.Step(
|
||||
level.lightProbes,
|
||||
renderEnvMap,
|
||||
renderEnvScreen
|
||||
);
|
||||
lightProbeBuilder.Step(level.lightProbes, renderEnvMap);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue