Prepare for new system and add Shader strict type abstraction

This commit is contained in:
2024-03-13 19:32:42 -04:00
parent ce4bef21b7
commit 4fdfac200d
21 changed files with 642 additions and 298 deletions

View File

@@ -185,18 +185,18 @@ namespace fgl::engine
camera_info[ frame_index ] = current_camera_info;
#if TRACY_ENABLE
m_culling_system.startPass( frame_info );
TracyVkCollect( frame_info.tracy_ctx, command_buffer );
m_culling_system.wait();
#else
m_culling_system.pass( frame_info );
#endif
m_renderer.beginSwapchainRendererPass( command_buffer );
// m_terrain_system.pass( frame_info );
m_entity_renderer.pass( frame_info );
m_composition_system.pass( frame_info );
#if ENABLE_IMGUI
{
TracyVkZone( tracy_ctx, command_buffer, "ImGui Rendering" );

View File

@@ -9,6 +9,8 @@
#include "engine/systems/CullingSystem.hpp"
#include "engine/systems/EntityRendererSystem.hpp"
#include "engine/tree/octtree/OctTreeNode.hpp"
#include "systems/CompositionSystem.hpp"
#include "systems/TerrainSystem.hpp"
namespace fgl::engine
{
@@ -25,7 +27,9 @@ namespace fgl::engine
OctTreeNode m_game_objects_root { WorldCoordinate( constants::WORLD_CENTER ) };
CullingSystem m_culling_system {};
//TerrainSystem m_terrain_system { Device::getInstance(), m_renderer.getSwapChainRenderPass() };
EntityRendererSystem m_entity_renderer { Device::getInstance(), m_renderer.getSwapChainRenderPass() };
CompositionSystem m_composition_system { Device::getInstance(), m_renderer.getSwapChainRenderPass() };
void loadGameObjects();

View File

@@ -27,11 +27,6 @@ namespace fgl::engine
}
};
template < typename T >
concept is_constant_range = requires( T t ) {
{
t.m_range
} -> std::same_as< const vk::PushConstantRange& >;
};
} // namespace fgl::engine

View File

@@ -4,8 +4,11 @@
#pragma once
#include "Descriptor.hpp"
#include "DescriptorPool.hpp"
#include "concepts.hpp"
#include "engine/concepts/is_bindable.hpp"
#include "engine/concepts/is_descriptor.hpp"
namespace fgl::engine
{
@@ -183,19 +186,7 @@ namespace fgl::engine
static_assert( TestSet::descriptor_count == 7 );
} // namespace internal
template < typename T >
concept is_descriptor_set = requires( std::remove_reference_t< T > t ) {
{
t.descriptor_count
} -> std::same_as< const std::uint16_t& >;
{
T::createLayout()
} -> std::same_as< vk::DescriptorSetLayout >;
};
template < typename T >
concept is_empty_descriptor_set = is_descriptor_set< T > && ( T::descriptor_count == 1 )
&& is_empty_descriptor< typename T::template Binding< 0 > >;
static_assert( is_descriptor_set< EmptyDescriptorSet< 0 > > && is_empty_descriptor_set< EmptyDescriptorSet< 0 > > );
static_assert( is_descriptor_set< internal::TestSet > && !is_empty_descriptor_set< internal::TestSet > );

View File

@@ -0,0 +1,34 @@
//
// Created by kj16609 on 3/13/24.
//
#pragma once
#include "engine/concepts/is_descriptor.hpp"
namespace fgl::engine
{
template < typename T >
concept is_descriptor_set = requires( std::remove_reference_t< T > t ) {
{
t.descriptor_count
} -> std::same_as< const std::uint16_t& >;
{
T::createLayout()
} -> std::same_as< vk::DescriptorSetLayout >;
};
template < typename T >
concept is_empty_descriptor_set = is_descriptor_set< T > && ( T::descriptor_count == 1 )
&& is_empty_descriptor< typename T::template Binding< 0 > >;
template < typename T >
concept is_constant_range = requires( T t ) {
{
t.m_range
} -> std::same_as< const vk::PushConstantRange& >;
};
template < typename T > concept is_valid_pipeline_input = is_constant_range< T > || is_descriptor_set< T >;
} // namespace fgl::engine

View File

@@ -7,53 +7,24 @@
#include <array>
#include <fstream>
#include "Shader.hpp"
#include "engine/model/Model.hpp"
namespace fgl::engine
namespace fgl::engine::internal
{
std::vector< std::byte > Pipeline::readFile( const std::filesystem::path& path )
{
if ( std::ifstream ifs( path, std::ios::binary | std::ios::ate ); ifs )
{
std::vector< std::byte > data;
data.resize( static_cast< std::size_t >( ifs.tellg() ) );
ifs.seekg( 0, std::ios::beg );
ifs.read( reinterpret_cast< char* >( data.data() ), static_cast< long >( data.size() ) );
return data;
}
else
throw std::runtime_error( "Failed to load file: " + path.string() );
}
void Pipeline::createGraphicsPipeline(
const std::filesystem::path& vert, const std::filesystem::path& frag, const PipelineConfigInfo& info )
std::vector< std::unique_ptr< ShaderHandle > >& shaders, const PipelineConfigInfo& info )
{
assert( info.render_pass != VK_NULL_HANDLE && "Cannot create graphics pipeline: no render pass provided" );
const std::vector< std::byte > vert_data { readFile( vert ) };
const std::vector< std::byte > frag_data { readFile( frag ) };
std::vector< vk::PipelineShaderStageCreateInfo > stages {};
createShaderModule( vert_data, &m_vert_shader );
createShaderModule( frag_data, &m_frag_shader );
vk::PipelineShaderStageCreateInfo shaderStages[ 2 ] {};
shaderStages[ 0 ].pNext = nullptr;
shaderStages[ 0 ].flags = {};
shaderStages[ 0 ].stage = vk::ShaderStageFlagBits::eVertex;
shaderStages[ 0 ].module = m_vert_shader;
shaderStages[ 0 ].pName = "main";
shaderStages[ 0 ].pSpecializationInfo = nullptr;
shaderStages[ 1 ].pNext = nullptr;
shaderStages[ 1 ].flags = {};
shaderStages[ 1 ].stage = vk::ShaderStageFlagBits::eFragment;
shaderStages[ 1 ].module = m_frag_shader;
shaderStages[ 1 ].pName = "main";
shaderStages[ 1 ].pSpecializationInfo = nullptr;
for ( const auto& shader : shaders )
{
stages.emplace_back( shader->stage_info );
}
assert( stages.size() >= 2 );
auto& binding_descriptions { info.binding_descriptions };
auto& attribute_descriptions { info.attribute_descriptions };
@@ -70,8 +41,8 @@ namespace fgl::engine
vk::GraphicsPipelineCreateInfo pipeline_info {};
pipeline_info.pNext = VK_NULL_HANDLE;
pipeline_info.flags = {};
pipeline_info.stageCount = 2;
pipeline_info.pStages = shaderStages;
pipeline_info.stageCount = static_cast< std::uint32_t >( stages.size() );
pipeline_info.pStages = stages.data();
pipeline_info.pVertexInputState = &vertex_input_info;
pipeline_info.pInputAssemblyState = &info.assembly_info;
pipeline_info.pTessellationState = VK_NULL_HANDLE;
@@ -94,33 +65,8 @@ namespace fgl::engine
m_vk_pipeline = temp.value;
}
void Pipeline::createShaderModule( const std::vector< std::byte >& code, vk::ShaderModule* module )
{
vk::ShaderModuleCreateInfo create_info {};
create_info.pNext = VK_NULL_HANDLE;
create_info.flags = {};
create_info.codeSize = code.size();
create_info.pCode = reinterpret_cast< const std::uint32_t* >( code.data() );
if ( m_device.device().createShaderModule( &create_info, nullptr, module ) != vk::Result::eSuccess )
throw std::runtime_error( "Failed to create shader module" );
}
Pipeline::Pipeline(
Device& device,
const std::filesystem::path& vert,
const std::filesystem::path& frag,
const PipelineConfigInfo& info ) :
m_device( device )
{
createGraphicsPipeline( vert, frag, info );
}
Pipeline::~Pipeline()
{
m_device.device().destroyShaderModule( m_vert_shader, nullptr );
m_device.device().destroyShaderModule( m_frag_shader, nullptr );
m_device.device().destroyPipelineLayout( m_layout, nullptr );
m_device.device().destroyPipeline( m_vk_pipeline, nullptr );
}
@@ -130,4 +76,4 @@ namespace fgl::engine
command_buffer.bindPipeline( vk::PipelineBindPoint::eGraphics, m_vk_pipeline );
}
} // namespace fgl::engine
} // namespace fgl::engine::internal

View File

@@ -11,6 +11,11 @@
#include "engine/rendering/Device.hpp"
namespace fgl::engine
{
struct ShaderHandle;
}
namespace fgl::engine::internal
{
class Pipeline
@@ -23,21 +28,11 @@ namespace fgl::engine
vk::ShaderModule m_vert_shader { VK_NULL_HANDLE };
vk::ShaderModule m_frag_shader { VK_NULL_HANDLE };
static std::vector< std::byte > readFile( const std::filesystem::path& path );
void createGraphicsPipeline(
const std::filesystem::path& vert, const std::filesystem::path& frag, const PipelineConfigInfo& info );
void createShaderModule( const std::vector< std::byte >& code, vk::ShaderModule* module );
std::vector< std::unique_ptr< ShaderHandle > >& shaders, const PipelineConfigInfo& info );
public:
Pipeline(
Device& device,
const std::filesystem::path& vert,
const std::filesystem::path& frag,
const PipelineConfigInfo& info );
Pipeline( Device& device ) : m_device( device ) {}
~Pipeline();
@@ -47,4 +42,4 @@ namespace fgl::engine
void bind( vk::CommandBuffer command_buffer );
};
} // namespace fgl::engine
} // namespace fgl::engine::internal

View File

@@ -9,6 +9,21 @@
namespace fgl::engine
{
void PipelineConfigInfo::setTriangleListTopo( PipelineConfigInfo& info )
{
info.assembly_info.topology = vk::PrimitiveTopology::eTriangleList;
}
void PipelineConfigInfo::setTriangleStripTopo( PipelineConfigInfo& info )
{
info.assembly_info.topology = vk::PrimitiveTopology::eTriangleStrip;
}
void PipelineConfigInfo::setPointPatch( PipelineConfigInfo& info )
{
info.assembly_info.topology = vk::PrimitiveTopology::ePatchList;
}
void PipelineConfigInfo::defaultConfig( PipelineConfigInfo& info )
{
info.viewport_info.viewportCount = 1;

View File

@@ -16,6 +16,8 @@ namespace fgl::engine
{
vk::PipelineViewportStateCreateInfo viewport_info {};
vk::PipelineInputAssemblyStateCreateInfo assembly_info {};
vk::PipelineTessellationStateCreateInfo tesselation_state_info {};
vk::PipelineTessellationDomainOriginStateCreateInfo tesselation_domain_info {};
vk::PipelineRasterizationStateCreateInfo rasterization_info {};
vk::PipelineMultisampleStateCreateInfo multisample_info {};
std::vector< vk::PipelineColorBlendAttachmentState > color_blend_attachment {};
@@ -37,6 +39,9 @@ namespace fgl::engine
PipelineConfigInfo& operator=( const PipelineConfigInfo& ) = delete;
static void disableVertexInput( PipelineConfigInfo& info );
static void setTriangleListTopo( PipelineConfigInfo& info );
static void setTriangleStripTopo( PipelineConfigInfo& info );
static void setPointPatch( PipelineConfigInfo& info );
static void defaultConfig( PipelineConfigInfo& info );
static void enableAlphaBlending( PipelineConfigInfo& config );
static void addColorAttachmentConfig( PipelineConfigInfo& info );

View File

@@ -5,22 +5,18 @@
#pragma once
#include "Pipeline.hpp"
#include "engine/PushConstant.hpp"
#include "engine/descriptors/Descriptor.hpp"
#include "Shader.hpp"
#include "engine/descriptors/DescriptorSet.hpp"
#include "engine/descriptors/DescriptorSetLayout.hpp"
namespace fgl::engine
{
template < typename T > concept is_valid_pipeline_input = is_constant_range< T > || is_descriptor_set< T >;
template <
std::uint16_t size,
std::uint16_t current_idx,
is_valid_pipeline_input CurrentSet,
is_valid_pipeline_input... Sets >
void createDescriptorSets( std::array< vk::DescriptorSetLayout, size >& out )
void createDescriptorSetsT( std::array< vk::DescriptorSetLayout, size >& out )
{
if constexpr ( size == 0 )
return;
@@ -34,12 +30,12 @@ namespace fgl::engine
out[ current_idx ] = CurrentSet::createDescriptorSetLayout();
assert( out[ current_idx ] != VK_NULL_HANDLE && "createDescriptorSetLayout returned VK_NULL_HANDLE" );
std::cout << "Created descriptor set layout for binding set " << current_idx << std::endl;
if constexpr ( sizeof...( Sets ) > 0 ) createDescriptorSets< size, current_idx + 1, Sets... >( out );
if constexpr ( sizeof...( Sets ) > 0 ) createDescriptorSetsT< size, current_idx + 1, Sets... >( out );
}
else if constexpr ( is_constant_range< CurrentSet > )
{
if constexpr ( sizeof...( Sets ) > 0 ) // We don't want to increase the size
createDescriptorSets< size, current_idx, Sets... >( out );
createDescriptorSetsT< size, current_idx, Sets... >( out );
else
return;
}
@@ -72,21 +68,19 @@ namespace fgl::engine
return getMaxBindingSetIDX< Sets... >();
}
template < typename... DescriptorSets >
requires( (is_valid_pipeline_input< DescriptorSets >) && ... )
class PipelineT : public Pipeline
template < is_valid_pipeline_input... DescriptorSets >
struct DescriptorSetCollection
{
using DescriptorSetTuple = std::tuple< DescriptorSets... >;
constexpr static std::uint64_t DescriptorSetCount { sizeof...( DescriptorSets ) };
//If the first descriptor set is a constant range, then the pipeline has a constant range
constexpr static bool has_constant_range {
is_constant_range< std::tuple_element_t< 0, std::tuple< DescriptorSets... > > >
};
constexpr static std::uint16_t binding_sets { ( is_descriptor_set< DescriptorSets > + ... ) };
constexpr static bool has_binding_sets { binding_sets != 0 };
//! Returns the binding type assocaited with the index
template < std::uint16_t binding_set_idx >
using BindingSet =
std::tuple_element_t< binding_set_idx + ( has_constant_range ? 1 : 0 ), std::tuple< DescriptorSets... > >;
constexpr static std::uint16_t binding_sets { ( is_descriptor_set< DescriptorSets > + ... ) };
constexpr static std::uint16_t max_binding_set { getMaxBindingSetIDX< DescriptorSets... >() };
@@ -94,6 +88,51 @@ namespace fgl::engine
constexpr static std::uint16_t empty_sets { ( is_empty_descriptor_set< DescriptorSets > + ... ) };
using LayoutArray = std::array< vk::DescriptorSetLayout, DescriptorSetCount - has_constant_range >;
static LayoutArray createDescriptorSets()
{
LayoutArray layouts;
createDescriptorSetsT< layouts.size(), 0, DescriptorSets... >( layouts );
return layouts;
}
template < std::uint64_t IDX >
requires( IDX < DescriptorSetCount )
using DescriptorSet = std::tuple_element_t< IDX, DescriptorSetTuple >;
template < std::uint64_t BindingIDX >
using BindingSet = DescriptorSet< BindingIDX + ( has_constant_range ? 1 : 0 ) >;
using PushConstantT = BindingSet< 0 >;
};
template < typename T >
concept is_descriptor_set_collection = requires( T t ) {
typename T::DescriptorSetTuple;
{
t.DescriptorSetCount
} -> std::same_as< const std::uint64_t& >;
};
template < is_shader_collection ShaderCollection, is_descriptor_set_collection DescriptorSetCollection >
class PipelineT : public internal::Pipeline
{
//If the first descriptor set is a constant range, then the pipeline has a constant range
constexpr static bool has_constant_range { DescriptorSetCollection::has_constant_range };
constexpr static std::uint16_t binding_sets { DescriptorSetCollection::binding_sets };
constexpr static bool has_binding_sets { binding_sets != 0 };
//! Returns the binding type assocaited with the index
template < std::uint16_t binding_set_idx >
using BindingSet = DescriptorSetCollection::template BindingSet< binding_set_idx >;
constexpr static std::uint16_t max_binding_set { DescriptorSetCollection::max_binding_set };
constexpr static std::uint16_t set_count { DescriptorSetCollection::set_count };
constexpr static std::uint16_t empty_sets { DescriptorSetCollection::empty_sets };
static_assert(
( set_count == 0 && has_constant_range ) || ( set_count == ( max_binding_set + 1 ) ),
"Binding sets must not have any spaces (Use EmptySet<idx>)" );
@@ -113,7 +152,7 @@ namespace fgl::engine
{
if constexpr ( has_constant_range )
{
return &std::tuple_element_t< 0, std::tuple< DescriptorSets... > >::m_range;
return &DescriptorSetCollection::template DescriptorSet< 0 >::m_range;
}
else
{
@@ -148,9 +187,7 @@ namespace fgl::engine
{
if ( m_layout != VK_NULL_HANDLE ) return m_layout;
std::array< vk::DescriptorSetLayout, sizeof...( DescriptorSets ) - has_constant_range > layouts;
createDescriptorSets< layouts.size(), 0, DescriptorSets... >( layouts );
typename DescriptorSetCollection::LayoutArray layouts { DescriptorSetCollection::createDescriptorSets() };
vk::PipelineLayoutCreateInfo pipeline_layout_info {};
pipeline_layout_info.setLayoutCount = has_binding_sets ? static_cast< uint32_t >( layouts.size() ) : 0;
@@ -186,7 +223,7 @@ namespace fgl::engine
{
if constexpr ( has_constant_range )
{
using PushConstantType = typename std::tuple_element_t< 0, std::tuple< DescriptorSets... > >;
using PushConstantType = typename DescriptorSetCollection::PushConstantT;
static_assert(
std::same_as< TPushData, typename PushConstantType::DataType >,
"Push constant data type mismatch" );
@@ -197,15 +234,13 @@ namespace fgl::engine
assert( "Attempted to push constant to pipeline without push constant range" );
}
PipelineT(
Device& device,
PipelineConfigInfo& info,
std::filesystem::path vertex_shader,
std::filesystem::path frag_shader ) :
Pipeline( device )
PipelineT( Device& device, PipelineConfigInfo& info ) : Pipeline( device )
{
populate( info, device );
createGraphicsPipeline( vertex_shader, frag_shader, info );
auto shaders { ShaderCollection::loadShaders() };
createGraphicsPipeline( shaders, info );
}
};

View File

@@ -0,0 +1,152 @@
//
// Created by kj16609 on 3/13/24.
//
#ifndef SHADER_HPP
#define SHADER_HPP
#include <fstream>
#include "engine/descriptors/concepts.hpp"
namespace fgl::engine
{
template < std::size_t N >
requires( N > 0 )
struct TString
{
using Character = std::string::value_type;
Character str[ N ];
constexpr TString( const char ( &literal )[ N ] ) { std::ranges::copy( literal, str ); }
consteval operator std::string_view() const noexcept { return std::string_view( str, N - 1 ); }
operator std::filesystem::path() const noexcept
{
return std::filesystem::path( std::string_view( str, N - 1 ) );
}
};
struct ShaderHandle
{
vk::PipelineShaderStageCreateInfo stage_info;
vk::ShaderModule shader_module;
ShaderHandle( const std::filesystem::path path, const vk::PipelineShaderStageCreateInfo info ) :
stage_info( info ),
shader_module( VK_NULL_HANDLE )
{
if ( auto ifs = std::ifstream( path, std::ios::binary | std::ios::ate ); ifs )
{
std::vector< std::byte > data;
data.resize( ifs.tellg() );
ifs.seekg( 0, std::ios::beg );
static_assert( sizeof( std::ifstream::char_type ) == sizeof( std::byte ) );
ifs.read( reinterpret_cast< std::ifstream::char_type* >( data.data() ), data.size() );
vk::ShaderModuleCreateInfo module_info {};
module_info.flags = {};
module_info.codeSize = data.size();
module_info.pCode = reinterpret_cast< const std::uint32_t* >( data.data() );
if ( Device::getInstance().device().createShaderModule( &module_info, nullptr, &shader_module )
!= vk::Result::eSuccess )
throw std::runtime_error( "Failed to create shader module" );
std::cout << "Created shader module for: " << path << std::endl;
stage_info.module = shader_module;
}
else
throw std::runtime_error( "Failed to load shader module. Path not found" );
}
ShaderHandle( const ShaderHandle& other ) = delete;
ShaderHandle& operator=( const ShaderHandle& other ) = delete;
ShaderHandle( ShaderHandle&& other ) = delete;
ShaderHandle& operator=( ShaderHandle&& other ) = delete;
~ShaderHandle()
{
if ( shader_module != VK_NULL_HANDLE ) Device::getInstance().device().destroyShaderModule( shader_module );
}
};
vk::ShaderModule loadShaderModule( const std::string_view path );
template < TString filepath, vk::ShaderStageFlagBits stage_flags >
struct Shader
{
consteval static vk::PipelineShaderStageCreateInfo defaultShaderInfo()
{
vk::PipelineShaderStageCreateInfo info {};
info.flags = {};
info.stage = stage_flags;
info.pName = "main";
info.pSpecializationInfo = nullptr;
return info;
}
static std::unique_ptr< ShaderHandle > load()
{
return std::make_unique< ShaderHandle >( filepath, defaultShaderInfo() );
}
virtual ~Shader() = default;
};
template < TString filepath >
using VertexShaderT = Shader< filepath, vk::ShaderStageFlagBits::eVertex >;
template < TString filepath >
using FragmentShaderT = Shader< filepath, vk::ShaderStageFlagBits::eFragment >;
template < typename T >
concept is_shader = requires( T t ) {
{
t.defaultShaderInfo()
} -> std::same_as< vk::PipelineShaderStageCreateInfo >;
};
template < is_shader... Shaders >
struct ShaderCollection
{
using ShaderTuple = std::tuple< Shaders... >;
constexpr static std::uint64_t ShaderCount { sizeof...( Shaders ) };
template < std::uint64_t IDX >
requires( IDX < ShaderCount )
using Shader = std::tuple_element_t< IDX, ShaderTuple >;
static_assert( ShaderCount >= 2, "Shader count must be two, Missing vertex or fragment?" );
static std::vector< std::unique_ptr< ShaderHandle > > loadShaders()
{
std::vector< std::unique_ptr< ShaderHandle > > shaders;
( ( shaders.push_back( Shaders::load() ) ), ... );
return shaders;
}
};
template < typename T >
concept is_shader_collection = requires( T t ) {
typename T::ShaderTuple;
{
t.ShaderCount
} -> std::same_as< const std::uint64_t& >;
};
} // namespace fgl::engine
#endif //SHADER_HPP

View File

@@ -28,6 +28,8 @@ namespace fgl::engine
public:
[[maybe_unused]] vk::CommandBuffer& setupSystem( FrameInfo& info );
CullingSystem() : m_thread( &CullingSystem::runner, this ) {}
void pass( FrameInfo& info );

View File

@@ -61,16 +61,7 @@ namespace fgl::engine
info.subpass = 0;
m_pipeline =
std::make_unique< Pipeline >( m_device, info, "shaders/gbuffer.vert.spv", "shaders/gbuffer.frag.spv" );
PipelineConfigInfo composition_info { render_pass };
PipelineConfigInfo::addColorAttachmentConfig( composition_info );
PipelineConfigInfo::disableVertexInput( composition_info );
composition_info.subpass = 1;
m_composition_pipeline = std::make_unique< CompositionPipeline >(
m_device, composition_info, "shaders/composition.vert.spv", "shaders/composition.frag.spv" );
m_pipeline = std::make_unique< Pipeline >( m_device, info );
using namespace fgl::literals::size_literals;
@@ -79,9 +70,6 @@ namespace fgl::engine
initDrawParameterBuffer( 1_KiB );
}
EntityRendererSystem::~EntityRendererSystem()
{}
using DrawPair = std::pair< vk::DrawIndexedIndirectCommand, std::vector< ModelMatrixInfo > >;
bool operator<( const DrawPair& left, const DrawPair& right )
@@ -94,168 +82,159 @@ namespace fgl::engine
return left.first.firstIndex == right.first.firstIndex && left.first.indexCount && right.first.indexCount;
}
vk::CommandBuffer& EntityRendererSystem::setupSystem( FrameInfo& info )
{
auto& command_buffer { info.command_buffer };
m_pipeline->bind( command_buffer );
m_pipeline->bindDescriptor( command_buffer, 0, info.global_descriptor_set );
m_pipeline->bindDescriptor( command_buffer, 1, Texture::getTextureDescriptorSet() );
return command_buffer;
}
void EntityRendererSystem::pass( FrameInfo& info )
{
ZoneScopedN( "Entity pass" );
auto& command_buffer { info.command_buffer };
auto& command_buffer { setupSystem( info ) };
TracyVkZone( info.tracy_ctx, command_buffer, "Render game objects" );
//TracyVkZone( m_device.getCurrentTracyCTX(), command_buffer, "Render game objects" );
std::unordered_map< DrawKey, DrawPair > draw_pairs;
std::uint64_t tri_counter { 0 };
std::uint64_t object_counter { 0 };
std::uint64_t primitive_counter { 0 };
for ( auto* node : info.in_view_leafs )
{
TracyVkZone( info.tracy_ctx, command_buffer, "Render game objects" );
//TracyVkZone( m_device.getCurrentTracyCTX(), command_buffer, "Render game objects" );
m_pipeline->bind( command_buffer );
m_pipeline->bindDescriptor( command_buffer, 0, info.global_descriptor_set );
m_pipeline->bindDescriptor( command_buffer, 1, Texture::getTextureDescriptorSet() );
std::unordered_map< DrawKey, DrawPair > draw_pairs;
std::uint64_t tri_counter { 0 };
std::uint64_t object_counter { 0 };
std::uint64_t primitive_counter { 0 };
for ( auto* node : info.in_view_leafs )
ZoneScopedN( "Process leaf" );
for ( const auto& obj : *node )
{
ZoneScopedN( "Process leaf" );
for ( const auto& obj : *node )
ZoneScopedN( "Process object" );
if ( obj.m_model == nullptr ) continue;
if ( !obj.m_is_visible ) continue;
// debug::world::drawBoundingBox( obj.getBoundingBox() );
++object_counter;
for ( const auto& primitive : obj.m_model->m_primitives )
{
ZoneScopedN( "Process object" );
if ( obj.m_model == nullptr ) continue;
++primitive_counter;
tri_counter += ( primitive.m_index_buffer.size() / 3 );
if ( !obj.m_is_visible ) continue;
const ModelMatrixInfo matrix_info { .model_matrix = obj.m_transform.mat4(),
.texture_idx = primitive.m_texture->getID() };
// debug::world::drawBoundingBox( obj.getBoundingBox() );
const auto key {
std::make_pair( primitive.m_texture->getID(), primitive.m_index_buffer.getOffset() )
};
++object_counter;
for ( const auto& primitive : obj.m_model->m_primitives )
if ( auto itter = draw_pairs.find( key ); itter != draw_pairs.end() )
{
++primitive_counter;
tri_counter += ( primitive.m_index_buffer.size() / 3 );
//Draw command for this mesh already exists. Simply add a count to it
auto& [ itter_key, pair ] = *itter;
auto& [ existing_cmd, model_matrix ] = pair;
const ModelMatrixInfo matrix_info { .model_matrix = obj.m_transform.mat4(),
.texture_idx = primitive.m_texture->getID() };
existing_cmd.instanceCount += 1;
model_matrix.emplace_back( matrix_info );
assert( model_matrix.size() == existing_cmd.instanceCount );
}
else
{
vk::DrawIndexedIndirectCommand cmd {};
const auto key {
std::make_pair( primitive.m_texture->getID(), primitive.m_index_buffer.getOffset() )
};
cmd.firstIndex = primitive.m_index_buffer.getOffsetCount();
cmd.indexCount = primitive.m_index_buffer.size();
if ( auto itter = draw_pairs.find( key ); itter != draw_pairs.end() )
{
//Draw command for this mesh already exists. Simply add a count to it
auto& [ itter_key, pair ] = *itter;
auto& [ existing_cmd, model_matrix ] = pair;
cmd.vertexOffset = static_cast< int32_t >( primitive.m_vertex_buffer.getOffsetCount() );
existing_cmd.instanceCount += 1;
model_matrix.emplace_back( matrix_info );
assert( model_matrix.size() == existing_cmd.instanceCount );
}
else
{
vk::DrawIndexedIndirectCommand cmd {};
cmd.instanceCount = 1;
cmd.firstIndex = primitive.m_index_buffer.getOffsetCount();
cmd.indexCount = primitive.m_index_buffer.size();
cmd.vertexOffset = static_cast< int32_t >( primitive.m_vertex_buffer.getOffsetCount() );
cmd.instanceCount = 1;
std::vector< ModelMatrixInfo > matrix_infos {};
matrix_infos.reserve( 128 );
matrix_infos.emplace_back( matrix_info );
draw_pairs.emplace( key, std::make_pair( cmd, std::move( matrix_infos ) ) );
}
std::vector< ModelMatrixInfo > matrix_infos {};
matrix_infos.reserve( 128 );
matrix_infos.emplace_back( matrix_info );
draw_pairs.emplace( key, std::make_pair( cmd, std::move( matrix_infos ) ) );
}
}
}
#if ENABLE_IMGUI
ImGui::Text( "Tris: %lu", tri_counter );
ImGui::Text( "Models: %lu", object_counter );
ImGui::Text( "Primitives: %lu", primitive_counter );
#endif
if ( draw_pairs.empty() )
{
std::cout << "Nothing to draw!" << std::endl;
command_buffer.nextSubpass( vk::SubpassContents::eInline );
return;
}
std::vector< vk::DrawIndexedIndirectCommand > draw_commands {};
std::vector< ModelMatrixInfo > model_matrices {};
draw_commands.reserve( draw_pairs.size() );
model_matrices.reserve( draw_pairs.size() * 2 );
TracyCZoneN( filter_zone_TRACY, "Reorganize draw commands", true );
for ( auto& [ key, pair ] : draw_pairs )
{
auto cmd { pair.first };
assert( cmd != vk::DrawIndexedIndirectCommand() );
cmd.firstInstance = static_cast< std::uint32_t >( model_matrices.size() );
assert( cmd.instanceCount == pair.second.size() );
assert( pair.second.size() > 0 );
draw_commands.emplace_back( cmd );
model_matrices.insert( model_matrices.end(), pair.second.begin(), pair.second.end() );
}
TracyCZoneEnd( filter_zone_TRACY );
//Setup model matrix info buffers
auto& model_matrix_info_buffer { m_model_matrix_info_buffers[ info.frame_idx ] };
model_matrix_info_buffer =
std::make_unique< ModelMatrixInfoBufferSuballocation >( info.model_matrix_info_buffer, model_matrices );
model_matrix_info_buffer->flush();
const auto& model_matricies_suballoc { model_matrix_info_buffer };
assert( model_matrix_info_buffer->size() == model_matrices.size() );
// Setup draw parameter buffer
TracyCZoneN( draw_zone_TRACY, "Submit draw data", true );
auto& draw_parameter_buffer { m_draw_parameter_buffers[ info.frame_idx ] };
draw_parameter_buffer =
std::make_unique< DrawParameterBufferSuballocation >( info.draw_parameter_buffer, draw_commands );
const auto& draw_params { draw_parameter_buffer };
assert( draw_params->size() == draw_commands.size() );
assert( draw_params->stride() == sizeof( vk::DrawIndexedIndirectCommand ) );
TracyCZoneEnd( draw_zone_TRACY );
draw_parameter_buffer->flush();
const std::vector< vk::Buffer > vertex_buffers { m_vertex_buffer->getVkBuffer(),
model_matricies_suballoc->getVkBuffer() };
command_buffer.bindVertexBuffers( 0, vertex_buffers, { 0, model_matricies_suballoc->getOffset() } );
command_buffer.bindIndexBuffer( m_index_buffer->getVkBuffer(), 0, vk::IndexType::eUint32 );
#if ENABLE_IMGUI
ImGui::Text( "Indirect draws: %lu", static_cast< std::size_t >( draw_params->size() ) );
#endif
command_buffer.drawIndexedIndirect(
draw_params->getVkBuffer(), draw_params->getOffset(), draw_params->size(), draw_params->stride() );
command_buffer.nextSubpass( vk::SubpassContents::eInline );
}
#if ENABLE_IMGUI
ImGui::Text( "Tris: %lu", tri_counter );
ImGui::Text( "Models: %lu", object_counter );
ImGui::Text( "Primitives: %lu", primitive_counter );
#endif
if ( draw_pairs.empty() )
{
//Composition pass
TracyVkZone( info.tracy_ctx, command_buffer, "Composition pass" );
m_composition_pipeline->bind( command_buffer );
m_composition_pipeline->bindDescriptor( command_buffer, 0, info.gbuffer_descriptor_set );
command_buffer.draw( 3, 1, 0, 0 );
std::cout << "Nothing to draw!" << std::endl;
return;
}
std::vector< vk::DrawIndexedIndirectCommand > draw_commands {};
std::vector< ModelMatrixInfo > model_matrices {};
draw_commands.reserve( draw_pairs.size() );
model_matrices.reserve( draw_pairs.size() * 2 );
TracyCZoneN( filter_zone_TRACY, "Reorganize draw commands", true );
for ( auto& [ key, pair ] : draw_pairs )
{
auto cmd { pair.first };
assert( cmd != vk::DrawIndexedIndirectCommand() );
cmd.firstInstance = static_cast< std::uint32_t >( model_matrices.size() );
assert( cmd.instanceCount == pair.second.size() );
assert( pair.second.size() > 0 );
draw_commands.emplace_back( cmd );
model_matrices.insert( model_matrices.end(), pair.second.begin(), pair.second.end() );
}
TracyCZoneEnd( filter_zone_TRACY );
//Setup model matrix info buffers
auto& model_matrix_info_buffer { m_model_matrix_info_buffers[ info.frame_idx ] };
model_matrix_info_buffer =
std::make_unique< ModelMatrixInfoBufferSuballocation >( info.model_matrix_info_buffer, model_matrices );
model_matrix_info_buffer->flush();
const auto& model_matricies_suballoc { model_matrix_info_buffer };
assert( model_matrix_info_buffer->size() == model_matrices.size() );
// Setup draw parameter buffer
TracyCZoneN( draw_zone_TRACY, "Submit draw data", true );
auto& draw_parameter_buffer { m_draw_parameter_buffers[ info.frame_idx ] };
draw_parameter_buffer =
std::make_unique< DrawParameterBufferSuballocation >( info.draw_parameter_buffer, draw_commands );
const auto& draw_params { draw_parameter_buffer };
assert( draw_params->size() == draw_commands.size() );
assert( draw_params->stride() == sizeof( vk::DrawIndexedIndirectCommand ) );
TracyCZoneEnd( draw_zone_TRACY );
draw_parameter_buffer->flush();
const std::vector< vk::Buffer > vertex_buffers { m_vertex_buffer->getVkBuffer(),
model_matricies_suballoc->getVkBuffer() };
command_buffer.bindVertexBuffers( 0, vertex_buffers, { 0, model_matricies_suballoc->getOffset() } );
command_buffer.bindIndexBuffer( m_index_buffer->getVkBuffer(), 0, vk::IndexType::eUint32 );
#if ENABLE_IMGUI
ImGui::Text( "Indirect draws: %lu", static_cast< std::size_t >( draw_params->size() ) );
#endif
command_buffer.drawIndexedIndirect(
draw_params->getVkBuffer(), draw_params->getOffset(), draw_params->size(), draw_params->stride() );
}
} // namespace fgl::engine

View File

@@ -22,11 +22,15 @@ namespace fgl::engine
{
Device& m_device;
using Pipeline = PipelineT< GlobalDescriptorSet, TextureDescriptorSet >;
using CompositionPipeline = PipelineT< GBufferDescriptorSet >;
using VertexShader = VertexShaderT< "shaders/gbuffer.vert.spv" >;
using FragmentShader = FragmentShaderT< "shaders/gbuffer.frag.spv" >;
using Shaders = ShaderCollection< VertexShader, FragmentShader >;
using DescriptorSets = DescriptorSetCollection< GlobalDescriptorSet, TextureDescriptorSet >;
using Pipeline = PipelineT< Shaders, DescriptorSets >;
std::unique_ptr< Pipeline > m_pipeline {};
std::unique_ptr< CompositionPipeline > m_composition_pipeline {};
std::unique_ptr< Buffer > m_vertex_buffer { nullptr };
std::unique_ptr< Buffer > m_index_buffer { nullptr };
@@ -57,6 +61,8 @@ namespace fgl::engine
vk::MemoryPropertyFlagBits::eDeviceLocal );
}
vk::CommandBuffer& setupSystem( FrameInfo& );
public:
Buffer& getVertexBuffer() { return *m_vertex_buffer; }
@@ -66,7 +72,7 @@ namespace fgl::engine
void pass( FrameInfo& info );
EntityRendererSystem( Device& device, VkRenderPass render_pass );
~EntityRendererSystem();
~EntityRendererSystem() = default;
EntityRendererSystem( EntityRendererSystem&& other ) = delete;
EntityRendererSystem( const EntityRendererSystem& other ) = delete;
EntityRendererSystem& operator=( const EntityRendererSystem& other ) = delete;

View File

@@ -5,6 +5,11 @@
#ifndef GAME_CONCEPTS_HPP
#define GAME_CONCEPTS_HPP
namespace vk
{
class CommandBuffer;
}
namespace fgl::engine
{
@@ -26,6 +31,9 @@ namespace fgl::engine
{
t.wait()
};
{
t.setupSystem( info )
} -> std::same_as< vk::CommandBuffer& >;
};
} // namespace fgl::engine