mirror of
https://github.com/barkeser2002/flower.git
synced 2026-09-25 14:56:14 +03:00
883 lines
29 KiB
C++
883 lines
29 KiB
C++
#include "../deferred_renderer.h"
|
|
#include "../render_scene.h"
|
|
#include "../renderer.h"
|
|
#include "../scene_textures.h"
|
|
#include "../../scene/component/landscape_component.h"
|
|
#include <asset/asset_texture.h>
|
|
#include <asset/asset_manager.h>
|
|
|
|
namespace engine
|
|
{
|
|
|
|
static AutoCVarFloat cVarTerrainLodContinueCoefficient(
|
|
"r.terrain.lod.continue", "coefficient of lod continue.", "Terrain", 1.5f, CVarFlags::ReadAndWrite);
|
|
|
|
static AutoCVarBool cVarTerrainDebugFrame(
|
|
"r.terrain.debugFrameware", "terrain render frameware.", "Terrain", false, CVarFlags::ReadAndWrite);
|
|
|
|
struct TerrainLODPreparePush
|
|
{
|
|
uint lodIndex; // from (frameData.landscape.lodCount - 1) to 1.
|
|
float coefficientLodContinue;
|
|
uint maxLodMipmap;
|
|
};
|
|
|
|
struct GPUDispatchIndirectCommand
|
|
{
|
|
uint32_t x;
|
|
uint32_t y;
|
|
uint32_t z;
|
|
uint32_t pad;
|
|
};
|
|
static_assert(sizeof(GPUDispatchIndirectCommand) % (4 * sizeof(float)) == 0);
|
|
|
|
struct HeightmapHzbPassPush
|
|
{
|
|
uint32_t kLoadSrcLevel = 1;
|
|
};
|
|
|
|
struct TerrainShadowDepthSDSMPush
|
|
{
|
|
uint32_t inCascadeId;
|
|
};
|
|
|
|
class TerrainPass : public PassInterface
|
|
{
|
|
public:
|
|
std::unique_ptr<ComputePipeResources> lodPrepare;
|
|
std::unique_ptr<ComputePipeResources> lodArgs;
|
|
std::unique_ptr<ComputePipeResources> patchArgs;
|
|
|
|
std::unique_ptr<ComputePipeResources> patchPrepare;
|
|
std::unique_ptr<ComputePipeResources> gbufferDrawArgs;
|
|
|
|
std::unique_ptr<GraphicPipeResources> gbuffer;
|
|
|
|
std::unique_ptr<ComputePipeResources> loadMap;
|
|
std::unique_ptr<ComputePipeResources> heightmapHzb;
|
|
|
|
std::unique_ptr<ComputePipeResources> heightmap2NormalMap;
|
|
|
|
std::unique_ptr<GraphicPipeResources> sdsmShadowDepth;
|
|
|
|
virtual void release() override
|
|
{
|
|
gbuffer.reset();
|
|
lodPrepare.reset();
|
|
patchPrepare.reset();
|
|
lodArgs.reset();
|
|
patchArgs.reset();
|
|
gbufferDrawArgs.reset();
|
|
heightmapHzb.reset();
|
|
loadMap.reset();
|
|
heightmap2NormalMap.reset();
|
|
sdsmShadowDepth.reset();
|
|
}
|
|
|
|
virtual void onInit() override
|
|
{
|
|
{
|
|
VkDescriptorSetLayout setLayout = VK_NULL_HANDLE;
|
|
getContext()->descriptorFactoryBegin()
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0) // frameData
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1) //
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 2)
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 3) //
|
|
.buildNoInfoPush(setLayout);
|
|
|
|
ShaderVariant shaderVariant("shader/terrain_shadow_depth.glsl");
|
|
shaderVariant.setStage(EShaderStage::eVertexShader);
|
|
|
|
ShaderVariant emptyFrag{ };
|
|
|
|
sdsmShadowDepth = std::make_unique<GraphicPipeResources>(
|
|
shaderVariant,
|
|
emptyFrag,
|
|
std::vector<VkDescriptorSetLayout>
|
|
{
|
|
setLayout,
|
|
m_context->getSamplerCache().getCommonDescriptorSetLayout(),
|
|
},
|
|
sizeof(TerrainShadowDepthSDSMPush),
|
|
std::vector<VkFormat>{ },
|
|
std::vector<VkPipelineColorBlendAttachmentState>{ },
|
|
GBufferTextures::depthTextureFormat(),
|
|
VK_CULL_MODE_NONE,
|
|
VK_COMPARE_OP_GREATER, true, true,
|
|
std::vector<VkVertexInputAttributeDescription>
|
|
{
|
|
{ 0, 0, VK_FORMAT_R32G32_SFLOAT, 0 },
|
|
},
|
|
sizeof(float) * 2);
|
|
}
|
|
|
|
{
|
|
VkDescriptorSetLayout setLayout = VK_NULL_HANDLE;
|
|
getContext()->descriptorFactoryBegin()
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0) // frameData
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1) //
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 2) //
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 3) //
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 4) //
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 5) //
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 6) //
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 7) //
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 8)
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 9) //
|
|
.buildNoInfoPush(setLayout);
|
|
|
|
std::vector<VkDescriptorSetLayout> setLayouts = {
|
|
setLayout,
|
|
};
|
|
|
|
|
|
{
|
|
ShaderVariant shaderVariant("shader/terrain_lod.glsl");
|
|
shaderVariant.setStage(EShaderStage::eComputeShader).setMacro(L"LOD_PREPARE_PASS");
|
|
|
|
lodPrepare = std::make_unique<ComputePipeResources>(
|
|
shaderVariant,
|
|
sizeof(TerrainLODPreparePush),
|
|
setLayouts);
|
|
}
|
|
|
|
{
|
|
ShaderVariant shaderVariant("shader/terrain_lod.glsl");
|
|
shaderVariant.setStage(EShaderStage::eComputeShader).setMacro(L"LOD_ARGS_PASS");
|
|
|
|
lodArgs = std::make_unique<ComputePipeResources>(
|
|
shaderVariant,
|
|
sizeof(TerrainLODPreparePush),
|
|
setLayouts);
|
|
}
|
|
|
|
{
|
|
ShaderVariant shaderVariant("shader/terrain_lod.glsl");
|
|
shaderVariant.setStage(EShaderStage::eComputeShader).setMacro(L"LOD_PATH_PASS");
|
|
|
|
patchArgs = std::make_unique<ComputePipeResources>(
|
|
shaderVariant,
|
|
sizeof(TerrainLODPreparePush),
|
|
setLayouts);
|
|
}
|
|
}
|
|
|
|
{
|
|
// Config code.
|
|
VkDescriptorSetLayout setLayout = VK_NULL_HANDLE;
|
|
getContext()->descriptorFactoryBegin()
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 0) // inDepth
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1) // hizFurthestImage
|
|
.buildNoInfoPush(setLayout);
|
|
|
|
heightmapHzb = std::make_unique<ComputePipeResources>("shader/terrain_heightMap_MinMaxGen.glsl",
|
|
(uint32_t)sizeof(HeightmapHzbPassPush), std::vector<VkDescriptorSetLayout>{ setLayout });
|
|
}
|
|
|
|
{
|
|
// Config code.
|
|
VkDescriptorSetLayout setLayout = VK_NULL_HANDLE;
|
|
getContext()->descriptorFactoryBegin()
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0) // frameData
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1) //
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 2) // hizFurthestImage
|
|
.buildNoInfoPush(setLayout);
|
|
|
|
loadMap = std::make_unique<ComputePipeResources>("shader/terrain_lod_patch_map.glsl", 0, std::vector<VkDescriptorSetLayout>{ setLayout });
|
|
}
|
|
|
|
{
|
|
// Config code.
|
|
VkDescriptorSetLayout setLayout = VK_NULL_HANDLE;
|
|
getContext()->descriptorFactoryBegin()
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 0) //
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1) // hizFurthestImage
|
|
.buildNoInfoPush(setLayout);
|
|
|
|
heightmap2NormalMap = std::make_unique<ComputePipeResources>("shader/heightfield2normal.glsl", 0, std::vector<VkDescriptorSetLayout>{ setLayout });
|
|
}
|
|
|
|
{
|
|
VkDescriptorSetLayout setLayout = VK_NULL_HANDLE;
|
|
getContext()->descriptorFactoryBegin()
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0) // frameData
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1) //
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 2) //
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 3) //
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 4) //
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 5) //
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 6)
|
|
.buildNoInfoPush(setLayout);
|
|
|
|
std::vector<VkDescriptorSetLayout> setLayouts = {
|
|
setLayout,
|
|
};
|
|
|
|
{
|
|
ShaderVariant shaderVariant("shader/terrain_patch.glsl");
|
|
shaderVariant.setStage(EShaderStage::eComputeShader).setMacro(L"PATCH_CULL_PASS");
|
|
|
|
patchPrepare = std::make_unique<ComputePipeResources>(
|
|
shaderVariant,
|
|
0,
|
|
setLayouts);
|
|
}
|
|
|
|
{
|
|
ShaderVariant shaderVariant("shader/terrain_patch.glsl");
|
|
shaderVariant.setStage(EShaderStage::eComputeShader).setMacro(L"DRAW_COMMAND_PASS");
|
|
|
|
gbufferDrawArgs = std::make_unique<ComputePipeResources>(
|
|
shaderVariant,
|
|
0,
|
|
setLayouts);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
{
|
|
VkDescriptorSetLayout layout = VK_NULL_HANDLE;
|
|
getContext()->descriptorFactoryBegin()
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0) // frameData
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1) //
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 2)
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 3)
|
|
.bindNoInfo(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 4)
|
|
.buildNoInfoPush(layout);
|
|
|
|
gbuffer = std::make_unique<GraphicPipeResources>(
|
|
"shader/terrain_gbuffer.glsl",
|
|
std::vector<VkDescriptorSetLayout>{
|
|
layout,
|
|
getContext()->getSamplerCache().getCommonDescriptorSetLayout(),
|
|
},
|
|
0,
|
|
std::vector<VkFormat>
|
|
{
|
|
GBufferTextures::hdrSceneColorFormat(),
|
|
GBufferTextures::gbufferAFormat(),
|
|
GBufferTextures::gbufferBFormat(),
|
|
GBufferTextures::gbufferSFormat(),
|
|
GBufferTextures::gbufferVFormat(),
|
|
GBufferTextures::gbufferIdFormat(),
|
|
},
|
|
std::vector<VkPipelineColorBlendAttachmentState>
|
|
{
|
|
RHIColorBlendAttachmentOpauqeState(),
|
|
RHIColorBlendAttachmentOpauqeState(),
|
|
RHIColorBlendAttachmentOpauqeState(),
|
|
RHIColorBlendAttachmentOpauqeState(),
|
|
RHIColorBlendAttachmentOpauqeState(),
|
|
RHIColorBlendAttachmentOpauqeState(),
|
|
},
|
|
GBufferTextures::depthTextureFormat(),
|
|
VK_CULL_MODE_FRONT_BIT,
|
|
VK_COMPARE_OP_GREATER, true, true,
|
|
std::vector<VkVertexInputAttributeDescription>
|
|
{
|
|
{ 0, 0, VK_FORMAT_R32G32_SFLOAT, 0 },
|
|
},
|
|
sizeof(float) * 2);
|
|
}
|
|
}
|
|
};
|
|
|
|
void LandscapeComponent::clearCache()
|
|
{
|
|
m_heightmapTextureUUID = { };
|
|
m_heightmapImage = nullptr;
|
|
|
|
m_heightMapHzb = nullptr;
|
|
|
|
m_maxHeight = 400.0f;
|
|
m_minHeight = -10.0f;
|
|
}
|
|
|
|
|
|
void LandscapeComponent::buildCache()
|
|
{
|
|
// Load height map image.
|
|
auto asset = std::dynamic_pointer_cast<AssetTexture>(getAssetManager()->getAsset(m_heightmapTextureUUID));
|
|
m_heightmapImage = asset->getGPUImage().lock();
|
|
}
|
|
|
|
|
|
bool LandscapeComponent::collectLandscape(RenderScene& renderScene, VkCommandBuffer cmd)
|
|
{
|
|
if (!m_heightmapTextureUUID.empty())
|
|
{
|
|
// Need load heightmap image.
|
|
if (m_heightmapImage == nullptr)
|
|
{
|
|
buildCache();
|
|
}
|
|
}
|
|
|
|
// Pre-generate mipmap.
|
|
if (m_heightmapImage != nullptr && m_heightmapImage->isAssetReady())
|
|
{
|
|
// Generate heightmap hzb when change here.
|
|
auto* pass = getContext()->getPasses().get<TerrainPass>();
|
|
auto* rtPool = &getContext()->getRenderTargetPools();
|
|
|
|
auto* image = m_heightmapImage->getReadyImage();
|
|
|
|
if (m_normalMap == nullptr)
|
|
{
|
|
m_normalMap = rtPool->createPoolImage(
|
|
"terrain_normalMap",
|
|
image->getExtent().width,
|
|
image->getExtent().height,
|
|
VK_FORMAT_A2B10G10R10_UNORM_PACK32,
|
|
VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT);
|
|
|
|
m_normalMap->getImage().transitionLayout(cmd, VK_IMAGE_LAYOUT_GENERAL);
|
|
|
|
{
|
|
pass->heightmap2NormalMap->bind(cmd);
|
|
|
|
PushSetBuilder(cmd)
|
|
.addSRV(*image)
|
|
.addUAV(m_normalMap)
|
|
.push(pass->heightmap2NormalMap.get());
|
|
|
|
vkCmdDispatch(cmd, getGroupCount(image->getExtent().width, 8), getGroupCount(image->getExtent().height, 8), 1);
|
|
}
|
|
|
|
m_normalMap->getImage().transitionLayout(cmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
|
}
|
|
|
|
// Need generate heightmap hzb.
|
|
if(m_heightMapHzb == nullptr)
|
|
{
|
|
uint32_t mipStartWidth = image->getExtent().width / 2;
|
|
uint32_t mipStartHeight = image->getExtent().height / 2;
|
|
|
|
m_heightMapHzb = rtPool->createPoolImage(
|
|
"HeightmapHzb",
|
|
mipStartWidth,
|
|
mipStartHeight,
|
|
VK_FORMAT_R16G16_UNORM,
|
|
VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT,
|
|
kRenderTextureFullMip);
|
|
|
|
// Build from src.
|
|
HeightmapHzbPassPush push{ .kLoadSrcLevel = 1 };
|
|
{
|
|
pass->heightmapHzb->bind(cmd);
|
|
pass->heightmapHzb->pushConst(cmd, &push);
|
|
{
|
|
VkImageSubresourceRange rangeMip0
|
|
{
|
|
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
|
.baseMipLevel = 0,
|
|
.levelCount = 1,
|
|
.baseArrayLayer = 0,
|
|
.layerCount = 1
|
|
};
|
|
|
|
m_heightMapHzb->getImage().transitionLayout(cmd, VK_IMAGE_LAYOUT_GENERAL, rangeMip0);
|
|
|
|
PushSetBuilder(cmd)
|
|
.addSRV(m_heightmapImage->getSelfImage(), rangeMip0)
|
|
.addUAV(m_heightMapHzb, rangeMip0)
|
|
.push(pass->heightmapHzb.get());
|
|
|
|
vkCmdDispatch(cmd, getGroupCount(mipStartWidth, 8), getGroupCount(mipStartHeight, 8), 1);
|
|
|
|
m_heightMapHzb->getImage().transitionLayout(cmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, rangeMip0);
|
|
}
|
|
}
|
|
|
|
// Build from hiz.
|
|
push.kLoadSrcLevel = 0;
|
|
pass->heightmapHzb->pushConst(cmd, &push);
|
|
if (m_heightMapHzb->getImage().getInfo().mipLevels > 1)
|
|
{
|
|
uint32_t loopWidth = mipStartWidth;
|
|
uint32_t loopHeight = mipStartHeight;
|
|
|
|
for (uint32_t i = 1; i < m_heightMapHzb->getImage().getInfo().mipLevels; i++)
|
|
{
|
|
VkImageSubresourceRange rangeMipN_1{
|
|
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = i - 1, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1 };
|
|
|
|
VkImageSubresourceRange rangeMipN{
|
|
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = i, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1 };
|
|
|
|
m_heightMapHzb->getImage().transitionLayout(cmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, rangeMipN_1);
|
|
m_heightMapHzb->getImage().transitionLayout(cmd, VK_IMAGE_LAYOUT_GENERAL, rangeMipN);
|
|
|
|
PushSetBuilder(cmd)
|
|
.addSRV(m_heightMapHzb, rangeMipN_1)
|
|
.addUAV(m_heightMapHzb, rangeMipN)
|
|
.push(pass->heightmapHzb.get());
|
|
|
|
|
|
loopWidth = math::max(1u, loopWidth / 2);
|
|
loopHeight = math::max(1u, loopHeight / 2);
|
|
vkCmdDispatch(cmd, getGroupCount(loopWidth, 8), getGroupCount(loopHeight, 8), 1);
|
|
}
|
|
}
|
|
|
|
|
|
m_heightMapHzb->getImage().transitionLayout(cmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, buildBasicImageSubresource());
|
|
}
|
|
}
|
|
|
|
// Only render when heightmap image exist.
|
|
return (m_heightmapImage != nullptr) && (m_heightMapHzb != nullptr) && m_heightmapImage->isAssetReady();
|
|
}
|
|
|
|
void engine::renderTerrainSDSMDepth(
|
|
VkCommandBuffer cmd,
|
|
BufferParameterHandle perFrameGPU,
|
|
GBufferTextures* inGBuffers,
|
|
RenderScene* scene,
|
|
SDSMInfos& sdsmInfo,
|
|
uint32_t cascadeId)
|
|
{
|
|
auto* landscape = scene->getLandscape();
|
|
if (landscape == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
auto* rtPool = &getContext()->getRenderTargetPools();
|
|
auto* pass = getContext()->getPasses().get<TerrainPass>();
|
|
|
|
auto& cascadeBuffer = sdsmInfo.cascadeInfoBuffer;
|
|
{
|
|
|
|
|
|
CHECK(inGBuffers->terrainPathDispatchBuffer != nullptr);
|
|
CHECK(inGBuffers->terrainLodCountBuffer != nullptr);
|
|
CHECK(inGBuffers->terrainLodNodeBuffer != nullptr);
|
|
|
|
TerrainShadowDepthSDSMPush push{};
|
|
push.inCascadeId = cascadeId;
|
|
|
|
pass->sdsmShadowDepth->bindAndPushConst(cmd, &push);
|
|
|
|
auto& patchBuffer = inGBuffers->terrainPatchBufferMainView;
|
|
auto& drawArgs = inGBuffers->terrainDrawArgsMainView;
|
|
|
|
PushSetBuilder(cmd)
|
|
.addBuffer(perFrameGPU)
|
|
.addBuffer(patchBuffer)
|
|
.addSRV(*landscape->getGPUImage()->getReadyImage())
|
|
.addBuffer(cascadeBuffer)
|
|
.push(pass->sdsmShadowDepth.get());
|
|
|
|
pass->sdsmShadowDepth->bindSet(cmd, std::vector<VkDescriptorSet>{
|
|
getContext()->getSamplerCache().getCommonDescriptorSet()
|
|
}, 1);
|
|
|
|
auto vB = getRenderer()->getSharedTextures().uniformGridVertices16x16->getVkBuffer();
|
|
const VkDeviceSize vBOffset = 0;
|
|
vkCmdBindVertexBuffers(cmd, 0, 1, &vB, &vBOffset);
|
|
|
|
vkCmdDrawIndirect(cmd, drawArgs->getBuffer()->getVkBuffer(),
|
|
0, 1, sizeof(uvec4));
|
|
}
|
|
}
|
|
|
|
void engine::prepareTerrainLODS(
|
|
VkCommandBuffer cmd,
|
|
GBufferTextures* inGBuffers,
|
|
RenderScene* scene,
|
|
BufferParameterHandle perFrameGPU,
|
|
GPUTimestamps* timer)
|
|
{
|
|
auto* landscape = scene->getLandscape();
|
|
if (landscape == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
auto* rtPool = &getContext()->getRenderTargetPools();
|
|
auto* pass = getContext()->getPasses().get<TerrainPass>();
|
|
|
|
uint lodCount = landscape->getLODCount();
|
|
uint maxLODCount = kTerrainCoarseNodeDim * kTerrainCoarseNodeDim * math::pow(4, int(lodCount) - 1);
|
|
|
|
auto readyLodNodeListBuffer = getContext()->getBufferParameters().getStaticStorage("ReadyTerrainLODNodeList", sizeof(uint) * 3 * maxLODCount);
|
|
auto readyLodNodeCount = getContext()->getBufferParameters().getStaticStorage("ReadyTerrainLODCount", sizeof(uint));
|
|
|
|
auto lodContinueCountersBuffer = getContext()->getBufferParameters().getStaticStorage("lodContinueCounters", sizeof(uint) * lodCount);
|
|
|
|
auto lodContinueBuffer0 = getContext()->getBufferParameters().getStaticStorage("lodContinueBuffer0", sizeof(uint) * 2 * maxLODCount);
|
|
auto lodContinueBuffer1 = getContext()->getBufferParameters().getStaticStorage("lodContinueBuffer1", sizeof(uint) * 2 * maxLODCount);
|
|
|
|
auto lodCmdBuffer = getContext()->getBufferParameters().getIndirectStorage("lodCmdBuffer", sizeof(GPUDispatchIndirectCommand));
|
|
auto patchCmdBuffer = getContext()->getBufferParameters().getIndirectStorage("patchCmdBuffer", sizeof(GPUDispatchIndirectCommand));
|
|
|
|
int lodNodeIdCount = (16 * (1 - pow(4, lodCount))) / -3;
|
|
auto lodNodeContinue = getContext()->getBufferParameters().getStaticStorage("lodNodeContinue", sizeof(uint) * lodNodeIdCount);
|
|
|
|
// Clear counter buffers.
|
|
vkCmdFillBuffer(cmd, *readyLodNodeCount->getBuffer(), 0, readyLodNodeCount->getBuffer()->getSize(), 0u);
|
|
vkCmdFillBuffer(cmd, *lodContinueCountersBuffer->getBuffer(), 0, lodContinueCountersBuffer->getBuffer()->getSize(), 0u);
|
|
|
|
{
|
|
std::array<VkBufferMemoryBarrier2, 2> fillBarriers
|
|
{
|
|
RHIBufferBarrier(readyLodNodeCount->getBuffer()->getVkBuffer(),
|
|
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT,
|
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT),
|
|
RHIBufferBarrier(readyLodNodeCount->getBuffer()->getVkBuffer(),
|
|
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT,
|
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT),
|
|
};
|
|
RHIPipelineBarrier(cmd, 0, (uint32_t)fillBarriers.size(), fillBarriers.data(), 0, nullptr);
|
|
|
|
}
|
|
|
|
uint32_t dim = math::pow(2, lodCount - 1) * 4;
|
|
|
|
auto patchLodMap = rtPool->createPoolImage(
|
|
"patchLodMap",
|
|
dim,
|
|
dim,
|
|
VK_FORMAT_R8_UNORM,
|
|
VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT);
|
|
|
|
|
|
// Get push const.
|
|
TerrainLODPreparePush pushConst { };
|
|
pushConst.coefficientLodContinue = cVarTerrainLodContinueCoefficient.get();
|
|
pushConst.maxLodMipmap = landscape->getHeightMapHZB()->getImage().getInfo().mipLevels - 1;
|
|
|
|
pass->lodPrepare->bind(cmd);
|
|
|
|
// Loop lods.
|
|
auto readBuffer = lodContinueBuffer0;
|
|
auto writeBuffer = lodContinueBuffer1;
|
|
bool bFirstPass = true;
|
|
for (int i = lodCount - 1; i >= 1; i--)
|
|
{
|
|
pushConst.lodIndex = i;
|
|
|
|
bool bFinalPass = i == 1;
|
|
|
|
// Add prepare args pass.
|
|
if (!bFirstPass)
|
|
{
|
|
|
|
PushSetBuilder(cmd)
|
|
.addBuffer(perFrameGPU)
|
|
.addBuffer(readyLodNodeListBuffer)
|
|
.addBuffer(readyLodNodeCount)
|
|
.addBuffer(lodContinueCountersBuffer)
|
|
.addBuffer(readBuffer)
|
|
.addBuffer(writeBuffer)
|
|
.addBuffer(lodCmdBuffer)
|
|
.addBuffer(patchCmdBuffer)
|
|
.addSRV(landscape->getHeightMapHZB())
|
|
.addBuffer(lodNodeContinue)
|
|
.push(pass->lodArgs.get());
|
|
|
|
pass->lodArgs->bindAndPushConst(cmd, &pushConst);
|
|
|
|
|
|
|
|
vkCmdDispatch(cmd, 1, 1, 1);
|
|
|
|
auto barrier = RHIBufferBarrier(lodCmdBuffer->getBuffer()->getVkBuffer(),
|
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_MEMORY_WRITE_BIT,
|
|
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_ACCESS_INDIRECT_COMMAND_READ_BIT | VK_ACCESS_SHADER_READ_BIT);
|
|
|
|
RHIPipelineBarrier(cmd, 0, 1, &barrier, 0, nullptr);
|
|
}
|
|
|
|
{
|
|
// push set.
|
|
PushSetBuilder(cmd)
|
|
.addBuffer(perFrameGPU)
|
|
.addBuffer(readyLodNodeListBuffer)
|
|
.addBuffer(readyLodNodeCount)
|
|
.addBuffer(lodContinueCountersBuffer)
|
|
.addBuffer(readBuffer)
|
|
.addBuffer(writeBuffer)
|
|
.addBuffer(lodCmdBuffer)
|
|
.addBuffer(patchCmdBuffer)
|
|
.addSRV(landscape->getHeightMapHZB())
|
|
.addBuffer(lodNodeContinue)
|
|
.push(pass->lodPrepare.get());
|
|
|
|
pass->lodPrepare->bindAndPushConst(cmd, &pushConst);
|
|
|
|
|
|
|
|
if (bFirstPass)
|
|
{
|
|
vkCmdDispatch(cmd, getGroupCount(kTerrainCoarseNodeDim * kTerrainCoarseNodeDim, 64), 1, 1);
|
|
}
|
|
else
|
|
{
|
|
vkCmdDispatchIndirect(cmd, lodCmdBuffer->getBuffer()->getVkBuffer(), 0);
|
|
}
|
|
|
|
std::array<VkBufferMemoryBarrier2, 2> endBufferBarriers
|
|
{
|
|
RHIBufferBarrier(readBuffer->getBuffer()->getVkBuffer(),
|
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_MEMORY_READ_BIT,
|
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT),
|
|
|
|
RHIBufferBarrier(writeBuffer->getBuffer()->getVkBuffer(),
|
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_MEMORY_WRITE_BIT,
|
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT),
|
|
};
|
|
RHIPipelineBarrier(cmd, 0, (uint32_t)endBufferBarriers.size(), endBufferBarriers.data(), 0, nullptr);
|
|
|
|
|
|
if (!bFirstPass && (!bFinalPass))
|
|
{
|
|
auto barrier = RHIBufferBarrier(lodCmdBuffer->getBuffer()->getVkBuffer(),
|
|
VK_ACCESS_INDIRECT_COMMAND_READ_BIT, VK_ACCESS_MEMORY_READ_BIT,
|
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_MEMORY_WRITE_BIT);
|
|
|
|
RHIPipelineBarrier(cmd, 0, 1, &barrier, 0, nullptr);
|
|
}
|
|
|
|
if (bFinalPass)
|
|
{
|
|
pass->patchArgs->bindAndPushConst(cmd, &pushConst);
|
|
|
|
PushSetBuilder(cmd)
|
|
.addBuffer(perFrameGPU)
|
|
.addBuffer(readyLodNodeListBuffer)
|
|
.addBuffer(readyLodNodeCount)
|
|
.addBuffer(lodContinueCountersBuffer)
|
|
.addBuffer(readBuffer)
|
|
.addBuffer(writeBuffer)
|
|
.addBuffer(lodCmdBuffer)
|
|
.addBuffer(patchCmdBuffer)
|
|
.addSRV(landscape->getHeightMapHZB())
|
|
.addBuffer(lodNodeContinue)
|
|
.push(pass->patchArgs.get());
|
|
|
|
|
|
|
|
|
|
|
|
vkCmdDispatch(cmd, 1, 1, 1);
|
|
|
|
auto barrier = RHIBufferBarrier(patchCmdBuffer->getBuffer()->getVkBuffer(),
|
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_MEMORY_WRITE_BIT,
|
|
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_ACCESS_INDIRECT_COMMAND_READ_BIT | VK_ACCESS_SHADER_READ_BIT);
|
|
|
|
RHIPipelineBarrier(cmd, 0, 1, &barrier, 0, nullptr);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Swap.
|
|
auto tempBuffer = readBuffer;
|
|
readBuffer = writeBuffer;
|
|
writeBuffer = tempBuffer;
|
|
bFirstPass = false;
|
|
}
|
|
|
|
// build patch lod map.
|
|
{
|
|
|
|
pass->loadMap->bind(cmd);
|
|
|
|
patchLodMap->getImage().transitionGeneral(cmd);
|
|
|
|
PushSetBuilder(cmd)
|
|
.addBuffer(perFrameGPU)
|
|
.addBuffer(lodNodeContinue)
|
|
.addUAV(patchLodMap)
|
|
.push(pass->loadMap.get());
|
|
|
|
vkCmdDispatch(cmd,
|
|
getGroupCount(patchLodMap->getImage().getExtent().width, 8),
|
|
getGroupCount(patchLodMap->getImage().getExtent().height, 8), 1);
|
|
|
|
patchLodMap->getImage().transitionShaderReadOnly(cmd);
|
|
}
|
|
|
|
|
|
|
|
inGBuffers->terrainLodNodeBuffer = readyLodNodeListBuffer;
|
|
inGBuffers->terrainLodCountBuffer = readyLodNodeCount;
|
|
inGBuffers->terrainPathDispatchBuffer = patchCmdBuffer;
|
|
inGBuffers->terrainLODPatchMap = patchLodMap;
|
|
|
|
{
|
|
auto patchCountBuffer = getContext()->getBufferParameters().getStaticStorage("patchCountBuffer", sizeof(uint));
|
|
auto patchBuffer = getContext()->getBufferParameters().getStaticStorage("patchBuffer", sizeof(TerrainPatch) * maxLODCount * 64);
|
|
|
|
|
|
|
|
// Clear counter buffers.
|
|
{
|
|
|
|
vkCmdFillBuffer(cmd, *patchCountBuffer->getBuffer(), 0, patchCountBuffer->getBuffer()->getSize(), 0u);
|
|
|
|
std::array<VkBufferMemoryBarrier2, 1> fillBarriers
|
|
{
|
|
RHIBufferBarrier(patchCountBuffer->getBuffer()->getVkBuffer(),
|
|
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT,
|
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT),
|
|
};
|
|
RHIPipelineBarrier(cmd, 0, (uint32_t)fillBarriers.size(), fillBarriers.data(), 0, nullptr);
|
|
}
|
|
|
|
|
|
auto gbufferDrawArgs = getContext()->getBufferParameters().getIndirectStorage("gbufferDrawArgs", sizeof(uint) * 4);
|
|
|
|
// Patch build for main view.
|
|
{
|
|
pass->patchPrepare->bind(cmd);
|
|
|
|
PushSetBuilder(cmd)
|
|
.addBuffer(perFrameGPU)
|
|
.addBuffer(inGBuffers->terrainLodNodeBuffer)
|
|
.addBuffer(inGBuffers->terrainLodCountBuffer)
|
|
.addBuffer(patchCountBuffer)
|
|
.addBuffer(patchBuffer)
|
|
.addBuffer(gbufferDrawArgs)
|
|
.addSRV(inGBuffers->terrainLODPatchMap)
|
|
.push(pass->patchPrepare.get());
|
|
|
|
vkCmdDispatchIndirect(cmd, inGBuffers->terrainPathDispatchBuffer->getBuffer()->getVkBuffer(), 0);
|
|
|
|
|
|
std::array<VkBufferMemoryBarrier2, 2> computeBarriers
|
|
{
|
|
RHIBufferBarrier(patchCountBuffer->getBuffer()->getVkBuffer(),
|
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_MEMORY_WRITE_BIT,
|
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT),
|
|
RHIBufferBarrier(patchBuffer->getBuffer()->getVkBuffer(),
|
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_MEMORY_WRITE_BIT,
|
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT),
|
|
};
|
|
RHIPipelineBarrier(cmd, 0, (uint32_t)computeBarriers.size(), computeBarriers.data(), 0, nullptr);
|
|
}
|
|
|
|
|
|
{
|
|
pass->gbufferDrawArgs->bind(cmd);
|
|
PushSetBuilder(cmd)
|
|
.addBuffer(perFrameGPU)
|
|
.addBuffer(inGBuffers->terrainLodNodeBuffer)
|
|
.addBuffer(inGBuffers->terrainLodCountBuffer)
|
|
.addBuffer(patchCountBuffer)
|
|
.addBuffer(patchBuffer)
|
|
.addBuffer(gbufferDrawArgs)
|
|
.addSRV(inGBuffers->terrainLODPatchMap)
|
|
.push(pass->gbufferDrawArgs.get());
|
|
|
|
vkCmdDispatch(cmd, 1, 1, 1);
|
|
|
|
std::array<VkBufferMemoryBarrier2, 1> computeBarriers
|
|
{
|
|
RHIBufferBarrier(gbufferDrawArgs->getBuffer()->getVkBuffer(),
|
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_MEMORY_WRITE_BIT,
|
|
VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT, VK_ACCESS_INDIRECT_COMMAND_READ_BIT),
|
|
};
|
|
RHIPipelineBarrier(cmd, 0, (uint32_t)computeBarriers.size(), computeBarriers.data(), 0, nullptr);
|
|
}
|
|
|
|
inGBuffers->terrainPatchBufferMainView = patchBuffer;
|
|
inGBuffers->terrainDrawArgsMainView = gbufferDrawArgs;
|
|
}
|
|
|
|
}
|
|
|
|
// Render terrain.
|
|
void engine::renderTerrainGbuffer(
|
|
VkCommandBuffer cmd,
|
|
GBufferTextures* inGBuffers,
|
|
RenderScene* scene,
|
|
BufferParameterHandle perFrameGPU,
|
|
GPUTimestamps* timer)
|
|
{
|
|
|
|
auto* landscape = scene->getLandscape();
|
|
if (landscape == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
CHECK(inGBuffers->terrainPathDispatchBuffer != nullptr);
|
|
CHECK(inGBuffers->terrainLodCountBuffer != nullptr);
|
|
CHECK(inGBuffers->terrainLodNodeBuffer != nullptr);
|
|
|
|
auto* pass = getContext()->getPasses().get<TerrainPass>();
|
|
uint lodCount = landscape->getLODCount();
|
|
uint maxLODCount = kTerrainCoarseNodeDim * kTerrainCoarseNodeDim * math::exp2(lodCount - 1);
|
|
|
|
|
|
auto& hdrSceneColor = inGBuffers->hdrSceneColor->getImage();
|
|
auto& gbufferA = inGBuffers->gbufferA->getImage();
|
|
auto& gbufferB = inGBuffers->gbufferB->getImage();
|
|
auto& gbufferS = inGBuffers->gbufferS->getImage();
|
|
auto& gbufferV = inGBuffers->gbufferV->getImage();
|
|
auto& gbufferId = inGBuffers->gbufferId->getImage();
|
|
auto& sceneDepthZ = inGBuffers->depthTexture->getImage();
|
|
|
|
hdrSceneColor.transitionLayout(cmd, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, RHIDefaultImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT));
|
|
gbufferA.transitionLayout(cmd, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, RHIDefaultImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT));
|
|
gbufferB.transitionLayout(cmd, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, RHIDefaultImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT));
|
|
gbufferS.transitionLayout(cmd, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, RHIDefaultImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT));
|
|
gbufferV.transitionLayout(cmd, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, RHIDefaultImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT));
|
|
gbufferId.transitionLayout(cmd, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, RHIDefaultImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT));
|
|
sceneDepthZ.transitionLayout(cmd, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, RHIDefaultImageSubresourceRange(VK_IMAGE_ASPECT_DEPTH_BIT));
|
|
|
|
std::vector<VkRenderingAttachmentInfo> colorAttachments = ColorAttachmentsBuilder()
|
|
.add(hdrSceneColor, VK_ATTACHMENT_LOAD_OP_LOAD)
|
|
.add(gbufferA, VK_ATTACHMENT_LOAD_OP_LOAD)
|
|
.add(gbufferB, VK_ATTACHMENT_LOAD_OP_LOAD)
|
|
.add(gbufferS, VK_ATTACHMENT_LOAD_OP_LOAD)
|
|
.add(gbufferV, VK_ATTACHMENT_LOAD_OP_LOAD)
|
|
.add(gbufferId, VK_ATTACHMENT_LOAD_OP_LOAD)
|
|
.result;
|
|
|
|
VkRenderingAttachmentInfo depthAttachment = getDepthAttachment(sceneDepthZ, VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE);
|
|
|
|
|
|
{
|
|
ScopeRenderCmdObject renderCmdScope(cmd, timer, "Terrain GBuffer", sceneDepthZ, colorAttachments, depthAttachment);
|
|
|
|
|
|
pass->gbuffer->bind(cmd);
|
|
PushSetBuilder(cmd)
|
|
.addBuffer(perFrameGPU)
|
|
.addBuffer(inGBuffers->terrainPatchBufferMainView)
|
|
.addSRV(*landscape->getGPUImage()->getReadyImage())
|
|
.addSRV(inGBuffers->terrainLODPatchMap)
|
|
.addSRV(landscape->getNormalMap())
|
|
.push(pass->gbuffer.get());
|
|
|
|
pass->gbuffer->bindSet(cmd, std::vector<VkDescriptorSet>{
|
|
getContext()->getSamplerCache().getCommonDescriptorSet()
|
|
}, 1);
|
|
|
|
auto vB = getRenderer()->getSharedTextures().uniformGridVertices16x16->getVkBuffer();
|
|
const VkDeviceSize vBOffset = 0;
|
|
vkCmdBindVertexBuffers(cmd, 0, 1, &vB, &vBOffset);
|
|
|
|
// Draw first.
|
|
if(!cVarTerrainDebugFrame.get())
|
|
{
|
|
cmdSetPolygonFillMode(cmd, VK_POLYGON_MODE_FILL);
|
|
vkCmdDrawIndirect(cmd, inGBuffers->terrainDrawArgsMainView->getBuffer()->getVkBuffer(),
|
|
0, 1, sizeof(uvec4));
|
|
}
|
|
else
|
|
{
|
|
cmdSetPolygonFillMode(cmd, VK_POLYGON_MODE_LINE);
|
|
vkCmdDrawIndirect(cmd, inGBuffers->terrainDrawArgsMainView->getBuffer()->getVkBuffer(),
|
|
0, 1, sizeof(uvec4));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|