392 lines
16 KiB
C++
392 lines
16 KiB
C++
#include "ge_vulkan_hiz_depth.hpp"
|
|
|
|
#include "ge_main.hpp"
|
|
#include "ge_vulkan_array_texture.hpp"
|
|
#include "ge_vulkan_attachment_texture.hpp"
|
|
#include "ge_vulkan_camera_scene_node.hpp"
|
|
#include "ge_vulkan_deferred_fbo.hpp"
|
|
#include "ge_vulkan_driver.hpp"
|
|
#include "ge_vulkan_shader_manager.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <stdexcept>
|
|
|
|
namespace GE
|
|
{
|
|
// ----------------------------------------------------------------------------
|
|
GEVulkanHiZDepth::GEVulkanHiZDepth(GEVulkanDriver* vk)
|
|
: m_vk(vk), m_hiz_depth(NULL),
|
|
m_descriptor_layout(VK_NULL_HANDLE),
|
|
m_pipeline_layout(VK_NULL_HANDLE),
|
|
m_pipeline(VK_NULL_HANDLE),
|
|
m_descriptor_pool(VK_NULL_HANDLE),
|
|
m_rendering_descriptor_pool(VK_NULL_HANDLE),
|
|
m_rendering_descriptor_set(VK_NULL_HANDLE)
|
|
{
|
|
} // GEVulkanHiZDepth
|
|
|
|
// ----------------------------------------------------------------------------
|
|
GEVulkanHiZDepth::~GEVulkanHiZDepth()
|
|
{
|
|
destroy();
|
|
} // ~GEVulkanHiZDepth
|
|
|
|
// ----------------------------------------------------------------------------
|
|
void GEVulkanHiZDepth::prepare(GEVulkanCameraSceneNode* cam)
|
|
{
|
|
irr::core::recti hiz_size(irr::core::position2di(
|
|
cam->getUBOData()->m_viewport.UpperLeftCorner.X,
|
|
cam->getUBOData()->m_viewport.UpperLeftCorner.Y),
|
|
irr::core::dimension2du(
|
|
cam->getUBOData()->m_viewport.LowerRightCorner.X,
|
|
cam->getUBOData()->m_viewport.LowerRightCorner.Y));
|
|
if (m_hiz_size != hiz_size)
|
|
{
|
|
m_hiz_size = hiz_size;
|
|
destroy();
|
|
init();
|
|
}
|
|
} // prepare
|
|
|
|
// ----------------------------------------------------------------------------
|
|
void GEVulkanHiZDepth::init()
|
|
{
|
|
m_hiz_depth = new GEVulkanArrayTexture(VK_FORMAT_R32_SFLOAT,
|
|
VK_IMAGE_VIEW_TYPE_2D,
|
|
irr::core::dimension2du(m_hiz_size.getWidth(), m_hiz_size.getHeight()),
|
|
1, irr::video::SColor(0), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
|
|
|
std::array<VkDescriptorSetLayoutBinding, 2> bindings = {};
|
|
// Input depth buffer
|
|
bindings[0].binding = 0;
|
|
bindings[0].descriptorCount = 1;
|
|
bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
|
bindings[0].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
// Output storage image
|
|
bindings[1].binding = 1;
|
|
bindings[1].descriptorCount = 1;
|
|
bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
|
bindings[1].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
|
|
VkDescriptorSetLayoutCreateInfo layout_info = {};
|
|
layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
|
layout_info.bindingCount = bindings.size();
|
|
layout_info.pBindings = bindings.data();
|
|
|
|
VkDevice device = m_vk->getDevice();
|
|
if (vkCreateDescriptorSetLayout(device, &layout_info, NULL,
|
|
&m_descriptor_layout) != VK_SUCCESS)
|
|
{
|
|
throw std::runtime_error("Failed to create descriptor set layout for "
|
|
"HiZ depth");
|
|
}
|
|
|
|
VkPushConstantRange push_constant = {};
|
|
push_constant.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
push_constant.offset = 0;
|
|
push_constant.size = sizeof(uint32_t) * 3;
|
|
|
|
VkPipelineLayoutCreateInfo pipeline_layout_info = {};
|
|
pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
|
pipeline_layout_info.setLayoutCount = 1;
|
|
pipeline_layout_info.pSetLayouts = &m_descriptor_layout;
|
|
pipeline_layout_info.pushConstantRangeCount = 1;
|
|
pipeline_layout_info.pPushConstantRanges = &push_constant;
|
|
|
|
if (vkCreatePipelineLayout(device, &pipeline_layout_info, NULL,
|
|
&m_pipeline_layout) != VK_SUCCESS)
|
|
{
|
|
throw std::runtime_error("Failed to create pipeline layout for HiZ "
|
|
"depth");
|
|
}
|
|
|
|
VkComputePipelineCreateInfo pipeline_info = {};
|
|
pipeline_info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
|
|
pipeline_info.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
|
pipeline_info.stage.stage = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
pipeline_info.stage.module = GEVulkanShaderManager::getShader("hiz_depth.comp");
|
|
pipeline_info.stage.pName = "main";
|
|
pipeline_info.layout = m_pipeline_layout;
|
|
|
|
if (vkCreateComputePipelines(device, VK_NULL_HANDLE, 1,
|
|
&pipeline_info, NULL, &m_pipeline) != VK_SUCCESS)
|
|
{
|
|
throw std::runtime_error("Failed to create compute pipeline for HiZ "
|
|
"depth");
|
|
}
|
|
|
|
// Create descriptor pool
|
|
const uint32_t mip_levels = m_hiz_depth->getMipmapLevels();
|
|
std::array<VkDescriptorPoolSize, 2> pool_sizes = {};
|
|
pool_sizes[0].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
|
pool_sizes[0].descriptorCount = mip_levels;
|
|
pool_sizes[1].type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
|
pool_sizes[1].descriptorCount = mip_levels;
|
|
|
|
VkDescriptorPoolCreateInfo pool_info = {};
|
|
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
|
pool_info.maxSets = mip_levels;
|
|
pool_info.poolSizeCount = pool_sizes.size();
|
|
pool_info.pPoolSizes = pool_sizes.data();
|
|
|
|
if (vkCreateDescriptorPool(device, &pool_info, NULL,
|
|
&m_descriptor_pool) != VK_SUCCESS)
|
|
{
|
|
throw std::runtime_error("Failed to create descriptor pool for HiZ depth");
|
|
}
|
|
|
|
// Create descriptor sets for each mip level
|
|
m_descriptor_sets.resize(mip_levels);
|
|
std::vector<VkDescriptorSetLayout> layouts(mip_levels,
|
|
m_descriptor_layout);
|
|
|
|
VkDescriptorSetAllocateInfo alloc_info = {};
|
|
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
|
alloc_info.descriptorPool = m_descriptor_pool;
|
|
alloc_info.descriptorSetCount = m_descriptor_sets.size();
|
|
alloc_info.pSetLayouts = layouts.data();
|
|
|
|
if (vkAllocateDescriptorSets(device, &alloc_info,
|
|
m_descriptor_sets.data()) != VK_SUCCESS)
|
|
{
|
|
throw std::runtime_error("Failed to allocate descriptor sets for HiZ "
|
|
"depth");
|
|
}
|
|
|
|
// Create image views for each mip level
|
|
m_hiz_views.resize(mip_levels);
|
|
for (uint32_t i = 0; i < m_hiz_views.size(); i++)
|
|
{
|
|
VkImageViewCreateInfo view_info = {};
|
|
view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
|
view_info.image = m_hiz_depth->getImage();
|
|
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
|
view_info.format = m_hiz_depth->getInternalFormat();
|
|
view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
view_info.subresourceRange.baseMipLevel = i;
|
|
view_info.subresourceRange.levelCount = 1;
|
|
view_info.subresourceRange.baseArrayLayer = 0;
|
|
view_info.subresourceRange.layerCount = 1;
|
|
|
|
if (vkCreateImageView(device, &view_info, NULL,
|
|
&m_hiz_views[i]) != VK_SUCCESS)
|
|
{
|
|
throw std::runtime_error("Failed to create image view for HiZ "
|
|
"depth");
|
|
}
|
|
}
|
|
|
|
GEVulkanDeferredFBO* dfbo =
|
|
static_cast<GEVulkanDeferredFBO*>(m_vk->getRTTTexture());
|
|
for (uint32_t i = 0; i < mip_levels; i++)
|
|
{
|
|
VkDescriptorImageInfo input_info = {};
|
|
input_info.imageLayout = i == 0 ?
|
|
VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL :
|
|
VK_IMAGE_LAYOUT_GENERAL;
|
|
input_info.imageView = i == 0 ?
|
|
(VkImageView)dfbo->getDepthTexture()->getTextureHandler() :
|
|
(VkImageView)m_hiz_depth->getTextureHandler();
|
|
input_info.sampler = m_vk->getSampler(GVS_SKYBOX);
|
|
|
|
VkDescriptorImageInfo output_info = {};
|
|
output_info.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
output_info.imageView = m_hiz_views[i];
|
|
|
|
std::array<VkWriteDescriptorSet, 2> descriptor_writes = {};
|
|
descriptor_writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
descriptor_writes[0].dstSet = m_descriptor_sets[i];
|
|
descriptor_writes[0].dstBinding = 0;
|
|
descriptor_writes[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
|
descriptor_writes[0].descriptorCount = 1;
|
|
descriptor_writes[0].pImageInfo = &input_info;
|
|
|
|
descriptor_writes[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
descriptor_writes[1].dstSet = m_descriptor_sets[i];
|
|
descriptor_writes[1].dstBinding = 1;
|
|
descriptor_writes[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
|
descriptor_writes[1].descriptorCount = 1;
|
|
descriptor_writes[1].pImageInfo = &output_info;
|
|
|
|
vkUpdateDescriptorSets(m_vk->getDevice(), descriptor_writes.size(),
|
|
descriptor_writes.data(), 0, NULL);
|
|
}
|
|
loadRenderingDescriptor();
|
|
} // init
|
|
|
|
// ----------------------------------------------------------------------------
|
|
void GEVulkanHiZDepth::loadRenderingDescriptor()
|
|
{
|
|
const size_t displace_binding_count = 3;
|
|
VkDescriptorPoolSize pool_size;
|
|
pool_size.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
|
pool_size.descriptorCount = displace_binding_count;
|
|
|
|
VkDescriptorPoolCreateInfo pool_info = {};
|
|
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
|
pool_info.flags = 0;
|
|
pool_info.maxSets = 1;
|
|
pool_info.poolSizeCount = 1;
|
|
pool_info.pPoolSizes = &pool_size;
|
|
if (vkCreateDescriptorPool(m_vk->getDevice(), &pool_info, NULL,
|
|
&m_rendering_descriptor_pool) != VK_SUCCESS)
|
|
{
|
|
throw std::runtime_error("vkCreateDescriptorPool failed for "
|
|
"m_rendering_descriptor_pool in GEVulkanHiZDepth");
|
|
}
|
|
|
|
GEVulkanDeferredFBO* dfbo =
|
|
static_cast<GEVulkanDeferredFBO*>(m_vk->getRTTTexture());
|
|
std::vector<VkDescriptorSetLayout> layouts(1,
|
|
dfbo->getDescriptorSetLayout(GVDFP_DISPLACE_COLOR));
|
|
|
|
VkDescriptorSetAllocateInfo alloc_info = {};
|
|
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
|
alloc_info.descriptorPool = m_rendering_descriptor_pool;
|
|
alloc_info.descriptorSetCount = layouts.size();
|
|
alloc_info.pSetLayouts = layouts.data();
|
|
|
|
if (vkAllocateDescriptorSets(m_vk->getDevice(), &alloc_info,
|
|
&m_rendering_descriptor_set) != VK_SUCCESS)
|
|
{
|
|
throw std::runtime_error("vkAllocateDescriptorSets failed for "
|
|
"m_rendering_descriptor_set in GEVulkanHiZDepth");
|
|
}
|
|
|
|
std::array<VkDescriptorImageInfo, displace_binding_count> image_infos = {};
|
|
image_infos[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
image_infos[0].imageView =
|
|
(VkImageView)
|
|
dfbo->getAttachment<GVDFT_DISPLACE_COLOR>()->getTextureHandler();
|
|
image_infos[0].sampler = m_vk->getSampler(GVS_NEAREST);
|
|
image_infos[1].imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
|
|
image_infos[1].imageView =
|
|
(VkImageView)dfbo->getDepthTexture()->getTextureHandler();
|
|
image_infos[1].sampler = m_vk->getSampler(GVS_SHADOW);
|
|
image_infos[2].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
image_infos[2].imageView =
|
|
(VkImageView)m_hiz_depth->getTextureHandler();
|
|
image_infos[2].sampler = m_vk->getSampler(GVS_SKYBOX);
|
|
|
|
VkWriteDescriptorSet write_descriptor_set = {};
|
|
write_descriptor_set.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
write_descriptor_set.dstBinding = 0;
|
|
write_descriptor_set.dstArrayElement = 0;
|
|
write_descriptor_set.descriptorType =
|
|
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
|
write_descriptor_set.descriptorCount = image_infos.size();
|
|
write_descriptor_set.pBufferInfo = 0;
|
|
write_descriptor_set.dstSet = m_rendering_descriptor_set;
|
|
write_descriptor_set.pImageInfo = image_infos.data();
|
|
|
|
vkUpdateDescriptorSets(m_vk->getDevice(), 1, &write_descriptor_set, 0,
|
|
NULL);
|
|
} // loadRenderingDescriptor
|
|
|
|
// ----------------------------------------------------------------------------
|
|
void GEVulkanHiZDepth::destroy()
|
|
{
|
|
delete m_hiz_depth;
|
|
m_hiz_depth = NULL;
|
|
VkDevice device = m_vk->getDevice();
|
|
if (m_pipeline != VK_NULL_HANDLE)
|
|
vkDestroyPipeline(device, m_pipeline, NULL);
|
|
m_pipeline = VK_NULL_HANDLE;
|
|
if (m_pipeline_layout != VK_NULL_HANDLE)
|
|
vkDestroyPipelineLayout(device, m_pipeline_layout, NULL);
|
|
m_pipeline_layout = VK_NULL_HANDLE;
|
|
if (m_rendering_descriptor_pool != VK_NULL_HANDLE)
|
|
vkDestroyDescriptorPool(device, m_rendering_descriptor_pool, NULL);
|
|
m_rendering_descriptor_pool = VK_NULL_HANDLE;
|
|
m_rendering_descriptor_set = VK_NULL_HANDLE;
|
|
if (m_descriptor_pool != VK_NULL_HANDLE)
|
|
vkDestroyDescriptorPool(device, m_descriptor_pool, NULL);
|
|
m_descriptor_pool = VK_NULL_HANDLE;
|
|
if (m_descriptor_layout != VK_NULL_HANDLE)
|
|
vkDestroyDescriptorSetLayout(device, m_descriptor_layout, NULL);
|
|
m_descriptor_layout = VK_NULL_HANDLE;
|
|
for (VkImageView view : m_hiz_views)
|
|
vkDestroyImageView(device, view, NULL);
|
|
m_hiz_views.clear();
|
|
m_descriptor_sets.clear();
|
|
} // destroy
|
|
|
|
// ----------------------------------------------------------------------------
|
|
void GEVulkanHiZDepth::generate(VkCommandBuffer cmd)
|
|
{
|
|
const uint32_t mip_levels = m_hiz_depth->getMipmapLevels();
|
|
|
|
VkImageMemoryBarrier barrier = {};
|
|
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
|
barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
|
barrier.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
|
|
barrier.oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
barrier.image = m_hiz_depth->getImage();
|
|
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
barrier.subresourceRange.baseMipLevel = 0;
|
|
barrier.subresourceRange.levelCount = mip_levels;
|
|
barrier.subresourceRange.baseArrayLayer = 0;
|
|
barrier.subresourceRange.layerCount = 1;
|
|
|
|
vkCmdPipelineBarrier(cmd,
|
|
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
|
0, 0, NULL, 0, NULL, 1, &barrier);
|
|
|
|
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, m_pipeline);
|
|
|
|
vkCmdPushConstants(cmd, m_pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT,
|
|
0, sizeof(uint32_t) * 2, &m_hiz_size.UpperLeftCorner.X);
|
|
|
|
for (uint32_t i = 0; i < mip_levels; i++)
|
|
{
|
|
// Bind descriptor set and push mip level constant
|
|
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_COMPUTE,
|
|
m_pipeline_layout, 0, 1, &m_descriptor_sets[i], 0, NULL);
|
|
|
|
vkCmdPushConstants(cmd, m_pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT,
|
|
sizeof(uint32_t) * 2, sizeof(uint32_t), &i);
|
|
|
|
// Calculate dispatch size based on current mip level dimensions
|
|
const uint32_t width = m_hiz_depth->getSize().Width >> i;
|
|
const uint32_t height = m_hiz_depth->getSize().Height >> i;
|
|
const uint32_t group_size_x = std::max(1u, (width + 15) / 16);
|
|
const uint32_t group_size_y = std::max(1u, (height + 15) / 16);
|
|
|
|
vkCmdDispatch(cmd, group_size_x, group_size_y, 1);
|
|
|
|
// Add memory barrier between mip level generations
|
|
if (i < mip_levels - 1)
|
|
{
|
|
barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
|
|
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
|
barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
barrier.subresourceRange.baseMipLevel = i;
|
|
barrier.subresourceRange.levelCount = 1;
|
|
|
|
vkCmdPipelineBarrier(cmd,
|
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
|
0, 0, NULL, 0, NULL, 1, &barrier);
|
|
}
|
|
}
|
|
|
|
barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
|
|
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
|
barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
barrier.subresourceRange.baseMipLevel = 0;
|
|
barrier.subresourceRange.levelCount = mip_levels;
|
|
|
|
vkCmdPipelineBarrier(cmd,
|
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
|
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
|
0, 0, NULL, 0, NULL, 1, &barrier);
|
|
} // generate
|
|
|
|
}
|