Prepare for new system and add Shader strict type abstraction
This commit is contained in:
@@ -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" );
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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 > );
|
||||
|
||||
34
src/engine/descriptors/concepts.hpp
Normal file
34
src/engine/descriptors/concepts.hpp
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
152
src/engine/pipeline/Shader.hpp
Normal file
152
src/engine/pipeline/Shader.hpp
Normal 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
|
||||
@@ -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 );
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user