RAIIify most of the logical device setup and usage

This commit is contained in:
2024-06-21 12:43:24 -04:00
parent dc18a4b273
commit 8e2fa623dd
63 changed files with 975 additions and 816 deletions

View File

@@ -9,11 +9,6 @@ add_library(FGLEngine STATIC ${CPP_SOURCES} ${HPP_SOURCES})
set(CMAKE_CXX_STANDARD 23)
target_precompile_headers(FGLEngine PRIVATE
<vector>
<iostream>
<array>
<chrono>
<optional>
<vulkan/vulkan.h>

View File

@@ -40,7 +40,7 @@ namespace fgl::engine
static Average< float, 60 * 15 > rolling_ms_average;
void preStage( vk::CommandBuffer& cmd_buffer )
void preStage( vk::raii::CommandBuffer& cmd_buffer )
{
ZoneScopedN( "Pre-Stage" );
@@ -155,7 +155,7 @@ namespace fgl::engine
camera_controller.moveInPlaneXZ( m_window.window(), delta_time, viewer );
camera.setView( viewer.getPosition(), viewer.getRotation() );
if ( auto command_buffer = m_renderer.beginFrame(); command_buffer )
if ( auto& command_buffer = m_renderer.beginFrame(); *command_buffer )
{
preStage( command_buffer );
@@ -188,7 +188,7 @@ namespace fgl::engine
camera_info[ frame_index ] = current_camera_info;
m_culling_system.startPass( frame_info );
TracyVkCollect( frame_info.tracy_ctx, command_buffer );
TracyVkCollect( frame_info.tracy_ctx, *command_buffer );
m_culling_system.wait();
m_renderer.beginSwapchainRendererPass( command_buffer );

View File

@@ -30,7 +30,7 @@ namespace fgl::engine
Device device { m_window, m_instance };
Renderer m_renderer { m_window };
Renderer m_renderer { m_window, device.phyDevice() };
//GameObject::Map game_objects {};
OctTreeNode m_game_objects_root { WorldCoordinate( constants::WORLD_CENTER ) };

View File

@@ -11,6 +11,8 @@
#define FGL_DELETE_MOVE_CTOR( ClassName ) ClassName( ClassName&& ) = delete;
#define FGL_DELETE_COPY( ClassName ) FGL_DELETE_COPY_CTOR( ClassName ) FGL_DELETE_COPY_ASSIGN( ClassName )
#define FGL_DELETE_MOVE( ClassName ) FGL_DELETE_MOVE_CTOR( ClassName ) FGL_DELETE_MOVE_ASSIGN( ClassName )
#define FGL_DELETE_ALL_Ro5( ClassName ) \
FGL_DELETE_DEFAULT_CTOR( ClassName ) FGL_DELETE_COPY( ClassName ) FGL_DELETE_MOVE( ClassName )
#ifndef FGL_FORCE_NOTHING

View File

@@ -70,7 +70,7 @@ namespace fgl::engine
{
std::uint16_t frame_idx;
float frame_time;
vk::CommandBuffer command_buffer;
vk::raii::CommandBuffer& command_buffer;
struct
{

View File

@@ -6,6 +6,7 @@
#include <vulkan/vulkan.hpp>
#include <vulkan/vulkan_raii.hpp>
namespace fgl::engine
{
@@ -21,7 +22,7 @@ namespace fgl::engine
static_assert( sizeof( T ) <= 128, "Push constant range size must be less or equal to 128 bytes" );
}
static void push( vk::CommandBuffer command_buffer, vk::PipelineLayout m_pipeline_layout, T& data )
static void push( vk::raii::CommandBuffer& command_buffer, vk::PipelineLayout m_pipeline_layout, T& data )
{
command_buffer.pushConstants( m_pipeline_layout, stages, offset, sizeof( T ), &data );
}

View File

@@ -5,7 +5,6 @@
#include "Window.hpp"
#include <vulkan/vulkan.hpp>
#include <vulkan/vulkan_handles.hpp>
#include <stdexcept>
@@ -42,13 +41,15 @@ namespace fgl::engine
glfwSetFramebufferSizeCallback( m_window, framebufferResizeCallback );
}
vk::SurfaceKHR Window::createWindowSurface( vk::Instance instance )
vk::raii::SurfaceKHR Window::createWindowSurface( Instance& instance )
{
VkSurfaceKHR temp_surface { VK_NULL_HANDLE };
if ( glfwCreateWindowSurface( instance, m_window, nullptr, &temp_surface ) != VK_SUCCESS )
throw std::runtime_error( "Failed to create window surface" );
return vk::SurfaceKHR( temp_surface );
vk::raii::SurfaceKHR surface { instance.handle(), temp_surface };
return surface;
}
void Window::framebufferResizeCallback( GLFWwindow* glfw_window, int width, int height )

View File

@@ -6,12 +6,12 @@
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include <vulkan/vulkan.hpp>
#include <vulkan/vulkan_handles.hpp>
#include <cstdint>
#include <string>
#include "rendering/Surface.hpp"
namespace fgl::engine
{
@@ -37,7 +37,7 @@ namespace fgl::engine
bool shouldClose() { return glfwWindowShouldClose( m_window ); }
vk::SurfaceKHR createWindowSurface( vk::Instance instance );
vk::raii::SurfaceKHR createWindowSurface( Instance& instance );
VkExtent2D getExtent()
{

View File

@@ -5,6 +5,7 @@
#pragma once
#include <vulkan/vulkan.hpp>
#include <vulkan/vulkan_raii.hpp>
#include <filesystem>
#include <memory>
@@ -20,7 +21,7 @@ namespace fgl::engine
struct AssetInterface
{
//! Stages the asset to the device (GPU)
virtual void stage( vk::CommandBuffer& buffer ) = 0;
virtual void stage( vk::raii::CommandBuffer& buffer ) = 0;
friend class AssetStore< T >;
@@ -76,7 +77,7 @@ namespace fgl::engine
//! Returns true if all items to be staged were submitted to the queue
//! Returns false if more items remain
bool stage( vk::CommandBuffer& buffer )
bool stage( vk::raii::CommandBuffer& buffer )
{
ZoneScoped;
std::lock_guard guard { queue_mtx };

View File

@@ -71,8 +71,9 @@ namespace fgl::engine
if ( range.size > m_byte_size ) range.size = VK_WHOLE_SIZE;
if ( Device::getInstance().device().flushMappedMemoryRanges( 1, &range ) != vk::Result::eSuccess )
throw std::runtime_error( "Failed to flush memory" );
std::vector< vk::MappedMemoryRange > ranges { range };
Device::getInstance()->flushMappedMemoryRanges( ranges );
}
Buffer& BufferSuballocation::getBuffer() const

View File

@@ -38,7 +38,7 @@ namespace fgl::engine
return *m_suballocations[ index ];
}
void bindForFrame( vk::CommandBuffer& cmd_buffer, std::uint16_t frame_idx )
void bindForFrame( vk::raii::CommandBuffer& cmd_buffer, std::uint16_t frame_idx )
{
m_suballocations[ frame_idx ].bind( cmd_buffer );
}

View File

@@ -34,7 +34,7 @@ namespace fgl::engine
HostVector< T >& getStaging() { return *m_staging_buffer; }
void stage( vk::CommandBuffer& command_buffer )
void stage( vk::raii::CommandBuffer& command_buffer )
{
assert( m_staging_buffer && "DeviceVector::stage() called without staging buffer" );

View File

@@ -5,6 +5,7 @@
#pragma once
#include <vulkan/vulkan.hpp>
#include <vulkan/vulkan_raii.hpp>
#include <concepts>
#include <cstdint>
@@ -18,6 +19,6 @@ namespace fgl::engine
} -> std::same_as< const std::uint16_t& >;
{
T::createLayout()
} -> std::same_as< vk::DescriptorSetLayout >;
} -> std::same_as< vk::raii::DescriptorSetLayout >;
};
} // namespace fgl::engine

View File

@@ -8,7 +8,8 @@
namespace fgl::engine
{
DescriptorPool::DescriptorPool( Device& device, std::uint32_t set_count )
vk::raii::DescriptorPool createPool( Device& device, std::uint32_t set_count )
{
std::vector< vk::DescriptorPoolSize > pool_sizes {};
for ( auto& [ type, ratio ] : descriptor_allocation_ratios )
@@ -23,22 +24,24 @@ namespace fgl::engine
pool_info.setFlags(
vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet | vk::DescriptorPoolCreateFlagBits::eUpdateAfterBind );
vk::Device vk_device { device.device() };
if ( vk_device.createDescriptorPool( &pool_info, nullptr, &m_pool ) != vk::Result::eSuccess )
throw std::runtime_error( "Failed to create descriptor pool" );
return device->createDescriptorPool( pool_info );
}
[[nodiscard]] vk::DescriptorSet DescriptorPool::allocateSet( vk::DescriptorSetLayout& layout )
DescriptorPool::DescriptorPool( Device& device, std::uint32_t set_count ) :
m_pool( createPool( device, set_count ) )
{}
[[nodiscard]] vk::raii::DescriptorSet DescriptorPool::allocateSet( vk::raii::DescriptorSetLayout& layout )
{
vk::DescriptorSet set;
vk::DescriptorSetAllocateInfo alloc_info {};
alloc_info.setDescriptorPool( m_pool );
alloc_info.setDescriptorSetCount( 1 );
alloc_info.setPSetLayouts( &layout );
alloc_info.setPSetLayouts( &( *layout ) );
vk::Device vk_device { Device::getInstance().device() };
if ( vk_device.allocateDescriptorSets( &alloc_info, &set ) != vk::Result::eSuccess )
throw std::runtime_error( "Failed to allocate descriptor set" );
std::vector< vk::raii::DescriptorSet > sets { Device::getInstance()->allocateDescriptorSets( alloc_info ) };
assert( sets.size() == 1 );
vk::raii::DescriptorSet set { std::move( sets[ 0 ] ) };
return set;
}
@@ -58,10 +61,4 @@ namespace fgl::engine
return *s_pool;
}
void DescriptorPool::deallocSet( vk::DescriptorSet& set )
{
vk::Device vk_device { Device::getInstance().device() };
vk_device.freeDescriptorSets( m_pool, 1, &set );
}
} // namespace fgl::engine

View File

@@ -5,6 +5,7 @@
#pragma once
#include <vulkan/vulkan.hpp>
#include <vulkan/vulkan_raii.hpp>
#include <cstdint>
#include <unordered_map>
@@ -19,7 +20,7 @@ namespace fgl::engine
class DescriptorPool
{
vk::DescriptorPool m_pool {};
vk::raii::DescriptorPool m_pool;
DescriptorPool( Device& device, std::uint32_t set_count );
@@ -30,24 +31,13 @@ namespace fgl::engine
DescriptorPool& operator=( const DescriptorPool& other ) = delete;
DescriptorPool& operator=( DescriptorPool&& other ) = delete;
vk::DescriptorPool getPool() const
{
assert( m_pool && "DescriptorPool::getVkPool() called on null pool" );
vk::raii::DescriptorPool& getPool() { return m_pool; }
return m_pool;
}
VkDescriptorPool getVkPool() const
{
assert( m_pool && "DescriptorPool::getVkPool() called on null pool" );
return m_pool;
}
VkDescriptorPool operator*() { return *m_pool; }
static DescriptorPool& init( Device& device );
[[nodiscard]] static DescriptorPool& getInstance();
[[nodiscard]] vk::DescriptorSet allocateSet( vk::DescriptorSetLayout& layout );
void deallocSet( vk::DescriptorSet& set );
[[nodiscard]] vk::raii::DescriptorSet allocateSet( vk::raii::DescriptorSetLayout& layout );
};
} // namespace fgl::engine

View File

@@ -14,16 +14,11 @@
namespace fgl::engine
{
DescriptorSet::DescriptorSet( vk::DescriptorSetLayout layout ) :
m_layout( layout ),
m_set( DescriptorPool::getInstance().allocateSet( layout ) )
DescriptorSet::DescriptorSet( vk::raii::DescriptorSetLayout&& layout ) :
m_layout( std::forward< vk::raii::DescriptorSetLayout >( layout ) ),
m_set( DescriptorPool::getInstance().allocateSet( m_layout ) )
{}
DescriptorSet::~DescriptorSet()
{
if ( m_set != VK_NULL_HANDLE ) DescriptorPool::getInstance().deallocSet( m_set );
}
DescriptorSet::DescriptorSet( DescriptorSet&& other ) :
m_infos( std::move( other.m_infos ) ),
descriptor_writes( std::move( other.descriptor_writes ) ),
@@ -67,7 +62,7 @@ namespace fgl::engine
}
void DescriptorSet::
bindImage( std::uint32_t binding_idx, ImageView& view, vk::ImageLayout layout, vk::Sampler sampler )
bindImage( std::uint32_t binding_idx, ImageView& view, vk::ImageLayout layout, vk::raii::Sampler sampler )
{
assert( binding_idx < m_infos.size() && "Binding index out of range" );
@@ -138,7 +133,7 @@ namespace fgl::engine
}
void DescriptorSet::
bindAttachment( std::uint32_t binding_idx, ImageView& view, vk::ImageLayout layout, vk::Sampler sampler )
bindAttachment( std::uint32_t binding_idx, ImageView& view, vk::ImageLayout layout, vk::raii::Sampler sampler )
{
assert( binding_idx < m_infos.size() && "Binding index out of range" );
@@ -163,7 +158,7 @@ namespace fgl::engine
vk::DebugUtilsObjectNameInfoEXT info {};
info.objectType = vk::ObjectType::eDescriptorSet;
info.pObjectName = str.c_str();
info.setObjectHandle( reinterpret_cast< std::uint64_t >( static_cast< VkDescriptorSet >( m_set ) ) );
info.setObjectHandle( reinterpret_cast< std::uint64_t >( getVkDescriptorSet() ) );
Device::getInstance().setDebugUtilsObjectName( info );
}

View File

@@ -26,8 +26,8 @@ namespace fgl::engine
std::vector< std::variant< std::shared_ptr< ImageView >, std::shared_ptr< BufferSuballocation > > >
m_resources {};
vk::DescriptorSetLayout m_layout;
vk::DescriptorSet m_set;
vk::raii::DescriptorSetLayout m_layout;
vk::raii::DescriptorSet m_set;
std::uint32_t m_max_idx { 0 };
@@ -39,11 +39,12 @@ namespace fgl::engine
void setMaxIDX( std::uint32_t max_idx );
vk::DescriptorSet& getSet() { return m_set; }
VkDescriptorSet operator*() { return *m_set; }
VkDescriptorSet getVkDescriptorSet() { return *m_set; }
DescriptorSet() = delete;
DescriptorSet( vk::DescriptorSetLayout layout );
~DescriptorSet();
DescriptorSet( vk::raii::DescriptorSetLayout&& layout );
//Copy
DescriptorSet( const DescriptorSet& other ) = delete;
@@ -54,12 +55,18 @@ namespace fgl::engine
DescriptorSet& operator=( DescriptorSet&& other );
void bindImage(
std::uint32_t binding_idx, ImageView& view, vk::ImageLayout layout, vk::Sampler sampler = VK_NULL_HANDLE );
std::uint32_t binding_idx,
ImageView& view,
vk::ImageLayout layout,
vk::raii::Sampler sampler = VK_NULL_HANDLE );
void bindUniformBuffer( std::uint32_t binding_idx, BufferSuballocation& buffer );
void bindAttachment(
std::uint32_t binding_idx, ImageView& view, vk::ImageLayout layout, vk::Sampler sampler = VK_NULL_HANDLE );
std::uint32_t binding_idx,
ImageView& view,
vk::ImageLayout layout,
vk::raii::Sampler sampler = VK_NULL_HANDLE );
void bindTexture( std::uint32_t binding_idx, std::shared_ptr< Texture >& tex_ptr );

View File

@@ -37,6 +37,7 @@ namespace fgl::engine
struct DescriptorSetCollection
{
using DescriptorSetTuple = std::tuple< DescriptorSets... >;
static constexpr auto SIZE { sizeof...( DescriptorSets ) };
constexpr static std::uint64_t DescriptorSetCount { sizeof...( DescriptorSets ) };
@@ -53,13 +54,13 @@ 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()
static std::vector< vk::raii::DescriptorSetLayout > createDescriptorSets()
{
LayoutArray layouts;
createDescriptorSetsT< layouts.size(), 0, DescriptorSets... >( layouts );
return layouts;
auto vec { createDescriptorSetsT< DescriptorSets... >() };
assert( vec.size() > 0 );
assert( vec.size() == binding_sets );
return createDescriptorSetsT< DescriptorSets... >();
}
template < std::uint64_t IDX >

View File

@@ -129,10 +129,8 @@ namespace fgl::engine
return extractBindingFlags< 0, 0 >();
}
static vk::DescriptorSetLayout createDescriptorSetLayout()
static vk::raii::DescriptorSetLayout createDescriptorSetLayout()
{
vk::DescriptorSetLayout layout { VK_NULL_HANDLE };
static constinit std::array< vk::DescriptorSetLayoutBinding, used_descriptor_count > bindings {
getLayoutBindings()
};
@@ -154,18 +152,14 @@ namespace fgl::engine
layout_info.pBindings = bindings.data();
layout_info.pNext = &flags_info;
if ( Device::getInstance().device().createDescriptorSetLayout( &layout_info, nullptr, &layout )
!= vk::Result::eSuccess )
throw std::runtime_error( "Failed to create descriptor set layout" );
return layout;
return Device::getInstance()->createDescriptorSetLayout( layout_info );
}
public:
DescriptorSetLayout() = delete;
static vk::DescriptorSetLayout createLayout() { return createDescriptorSetLayout(); }
static vk::raii::DescriptorSetLayout createLayout() { return createDescriptorSetLayout(); }
};
template < std::uint16_t set_idx >

View File

@@ -4,46 +4,62 @@
#pragma once
#include <cstdint>
#include <iostream>
#include "engine/concepts/is_valid_pipeline_input.hpp"
namespace fgl::engine
{
template <
std::uint16_t size,
std::uint16_t current_idx,
is_valid_pipeline_input CurrentSet,
is_valid_pipeline_input... Sets >
void createDescriptorSetsT( std::array< vk::DescriptorSetLayout, size >& out )
template < is_valid_pipeline_input CurrentSet, is_valid_pipeline_input... Sets >
std::vector< vk::raii::DescriptorSetLayout > createDescriptorSetsT( std::vector< vk::raii::DescriptorSetLayout >
out )
{
if constexpr ( size == 0 )
return;
if constexpr ( is_descriptor_set< CurrentSet > )
{
vk::raii::DescriptorSetLayout layout { CurrentSet::createDescriptorSetLayout() };
out.emplace_back( std::move( layout ) );
}
else if constexpr ( is_constant_range< CurrentSet > )
{
//noop
}
else
{
static_assert( size > 0, "Size must be greater than 0" );
static_assert( current_idx < size, "Current index must be less than size" );
if constexpr ( is_descriptor_set< CurrentSet > )
{
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 ) 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
createDescriptorSetsT< size, current_idx, Sets... >( out );
else
return;
}
else
{
static_assert( false, "Invalid input" );
}
static_assert( false, "Invalid input" );
}
if constexpr ( sizeof...( Sets ) > 0 )
{
return createDescriptorSetsT< Sets... >( std::move( out ) );
}
return out;
}
template < is_valid_pipeline_input CurrentSet, is_valid_pipeline_input... Sets >
std::vector< vk::raii::DescriptorSetLayout > createDescriptorSetsT()
{
std::vector< vk::raii::DescriptorSetLayout > out {};
if constexpr ( is_descriptor_set< CurrentSet > )
{
vk::raii::DescriptorSetLayout layout { CurrentSet::createDescriptorSetLayout() };
out.emplace_back( std::move( layout ) );
}
else if constexpr ( is_constant_range< CurrentSet > )
{
//noop
}
else
{
static_assert( false, "Invalid input" );
}
if constexpr ( sizeof...( Sets ) > 0 )
{
return createDescriptorSetsT< Sets... >( std::move( out ) );
}
return out;
}
} // namespace fgl::engine

View File

@@ -36,10 +36,6 @@ namespace fgl::engine::filesystem
file_texture = getTextureStore().load( "./assets/file.png", vk::Format::eR8G8B8A8Unorm );
up_texture = getTextureStore().load( "./assets/up.png", vk::Format::eR8G8B8A8Unorm );
auto cmd_buffer { Device::getInstance().beginSingleTimeCommands() };
Device::getInstance().endSingleTimeCommands( cmd_buffer );
current = DirInfo( test_path );
}

View File

@@ -39,26 +39,28 @@ namespace fgl::engine::gui
Device& device { Device::getInstance() };
ImGui_ImplGlfw_InitForVulkan( window.window(), true );
ImGui_ImplVulkan_InitInfo init_info { .Instance = device.instance(),
.PhysicalDevice = device.phyDevice(),
.Device = device.device(),
.QueueFamily = device.findPhysicalQueueFamilies().graphicsFamily,
.Queue = device.graphicsQueue(),
.DescriptorPool = DescriptorPool::getInstance().getVkPool(),
.RenderPass = renderer.getSwapChainRenderPass(),
.MinImageCount = 2,
.ImageCount = 2,
.MSAASamples = VK_SAMPLE_COUNT_1_BIT,
ImGui_ImplVulkan_InitInfo init_info {
.Instance = device.instance(),
.PhysicalDevice = *device.phyDevice().handle(),
.Device = *device,
.QueueFamily = device.phyDevice().queueInfo().getIndex( vk::QueueFlagBits::eGraphics ),
.Queue = *device.graphicsQueue(),
.DescriptorPool = *DescriptorPool::getInstance().getPool(),
.RenderPass = *renderer.getSwapChainRenderPass(),
.MinImageCount = 2,
.ImageCount = 2,
.MSAASamples = VK_SAMPLE_COUNT_1_BIT,
.PipelineCache = VK_NULL_HANDLE,
.Subpass = 1,
.PipelineCache = VK_NULL_HANDLE,
.Subpass = 1,
.UseDynamicRendering = VK_FALSE,
.PipelineRenderingCreateInfo = {},
.UseDynamicRendering = VK_FALSE,
.PipelineRenderingCreateInfo = {},
.Allocator = VK_NULL_HANDLE,
.CheckVkResultFn = VK_NULL_HANDLE,
.MinAllocationSize = 1024 * 1024 };
.Allocator = VK_NULL_HANDLE,
.CheckVkResultFn = VK_NULL_HANDLE,
.MinAllocationSize = 1024 * 1024
};
ImGui_ImplVulkan_Init( &init_info );
}
@@ -74,19 +76,18 @@ namespace fgl::engine::gui
ImGui::Begin( "Main" );
}
void endImGui( vk::CommandBuffer& command_buffer )
void endImGui( vk::raii::CommandBuffer& command_buffer )
{
ImGui::End();
ImGui::Render();
ImDrawData* data { ImGui::GetDrawData() };
ImGui_ImplVulkan_RenderDrawData( data, command_buffer );
ImGui_ImplVulkan_RenderDrawData( data, *command_buffer );
//ImGui::UpdatePlatformWindows();
//ImGui::RenderPlatformWindowsDefault();
}
void drawMainGUI( FrameInfo& info )
{
ZoneScoped;

View File

@@ -26,9 +26,4 @@ namespace fgl::engine
m_handle->setName( str );
}
vk::Image& Image::getVkImage()
{
return m_handle->m_image;
}
} // namespace fgl::engine

View File

@@ -7,41 +7,19 @@
namespace fgl::engine
{
ImageHandle::ImageHandle( fgl::engine::ImageHandle&& other ) noexcept :
m_allocation( other.m_allocation ),
m_allocation_info( other.m_allocation_info ),
m_image( other.m_image ),
m_extent( other.m_extent ),
m_format( other.m_format ),
m_usage( other.m_usage )
{
other.m_allocation = VK_NULL_HANDLE;
other.m_image = VK_NULL_HANDLE;
other.m_allocation_info = {};
other.m_image = VK_NULL_HANDLE;
}
ImageHandle::ImageHandle(
const vk::Extent2D extent, const vk::Format format, vk::Image image, vk::ImageUsageFlags usage ) noexcept :
m_image( image ),
m_extent( extent ),
m_format( format ),
m_usage( usage )
{}
ImageHandle::ImageHandle(
const vk::Extent2D extent,
const vk::Format format,
vk::ImageUsageFlags usage,
vk::ImageLayout inital_layout,
vk::ImageLayout final_layout ) :
m_extent( extent ),
m_format( format ),
m_usage( usage ),
m_initial_layout( inital_layout ),
m_final_layout( final_layout )
m_image( image )
{
assert( std::get< vk::Image >( m_image ) != VK_NULL_HANDLE );
}
vk::raii::Image createImage(
const vk::Extent2D extent, const vk::Format format, vk::ImageLayout inital_layout, vk::ImageUsageFlags usage )
{
ZoneScoped;
vk::ImageCreateInfo image_info {};
image_info.imageType = vk::ImageType::e2D;
@@ -61,9 +39,28 @@ namespace fgl::engine
image_info.samples = vk::SampleCountFlagBits::e1;
image_info.sharingMode = vk::SharingMode::eExclusive;
if ( Device::getInstance().device().createImage( &image_info, nullptr, &m_image ) != vk::Result::eSuccess )
throw std::runtime_error( "Failed to create image" );
return Device::getInstance()->createImage( image_info );
}
ImageHandle::ImageHandle(
const vk::Extent2D extent,
const vk::Format format,
vk::ImageUsageFlags usage,
vk::ImageLayout inital_layout,
vk::ImageLayout final_layout ) :
m_extent( extent ),
m_format( format ),
m_usage( usage ),
m_initial_layout( inital_layout ),
m_final_layout( final_layout ),
m_image( createImage( extent, format, inital_layout, usage ) )
{
assert( std::holds_alternative< vk::raii::Image >( m_image ) );
assert( *std::get< vk::raii::Image >( m_image ) != VK_NULL_HANDLE );
ZoneScoped;
//Allocate memory for image
VmaAllocationCreateInfo alloc_info {};
alloc_info.usage = VMA_MEMORY_USAGE_GPU_ONLY;
@@ -82,7 +79,7 @@ namespace fgl::engine
info.objectType = vk::ObjectType::eImage;
info.pObjectName = str.c_str();
info.setObjectHandle( reinterpret_cast< uint64_t >( static_cast< VkImage >( m_image ) ) );
info.setObjectHandle( reinterpret_cast< uint64_t >( getVkImage() ) );
Device::getInstance().setDebugUtilsObjectName( info );
}

View File

@@ -6,6 +6,7 @@
#include <memory>
#include "engine/logging/logging.hpp"
#include "engine/rendering/Device.hpp"
#include "vma/vma_impl.hpp"
#include "vulkan/vulkan.hpp"
@@ -19,8 +20,6 @@ namespace fgl::engine
VmaAllocation m_allocation { VK_NULL_HANDLE };
VmaAllocationInfo m_allocation_info {};
vk::Image m_image { VK_NULL_HANDLE };
vk::Extent2D m_extent;
vk::Format m_format;
vk::ImageUsageFlags m_usage;
@@ -28,17 +27,15 @@ namespace fgl::engine
vk::ImageLayout m_initial_layout { vk::ImageLayout::eUndefined };
vk::ImageLayout m_final_layout { vk::ImageLayout::eUndefined };
// Because of the way the swapchain works we need to be able to storage a `VkImage` handle.
std::variant< vk::raii::Image, vk::Image > m_image;
friend class ImageView;
friend class Image;
public:
ImageHandle() = delete;
ImageHandle( const ImageHandle& other ) = delete;
ImageHandle& operator=( const ImageHandle& other ) = delete;
ImageHandle( ImageHandle&& other ) noexcept;
FGL_DELETE_ALL_Ro5( ImageHandle );
ImageHandle(
const vk::Extent2D extent, const vk::Format format, vk::Image image, vk::ImageUsageFlags usage ) noexcept;
@@ -52,7 +49,18 @@ namespace fgl::engine
void setName( const std::string str );
vk::Image& getVkImage() { return m_image; }
VkImage operator*()
{
ZoneScoped;
if ( std::holds_alternative< vk::raii::Image >( m_image ) )
{
return *std::get< vk::raii::Image >( m_image );
}
else
return std::get< vk::Image >( m_image );
}
VkImage getVkImage() { return **this; }
vk::Format format() const { return m_format; }
@@ -77,7 +85,9 @@ namespace fgl::engine
~ImageHandle()
{
if ( m_allocation != VK_NULL_HANDLE )
vmaDestroyImage( Device::getInstance().allocator(), m_image, m_allocation );
{
vmaFreeMemory( Device::getInstance().allocator(), m_allocation );
}
}
};

View File

@@ -7,30 +7,31 @@
namespace fgl::engine
{
vk::raii::ImageView ImageView::createImageView( const std::shared_ptr< ImageHandle >& img )
{
vk::ImageViewCreateInfo info {};
info.image = **img;
info.viewType = vk::ImageViewType::e2D;
info.format = img->format();
info.subresourceRange.aspectMask = img->aspectMask();
info.subresourceRange.baseMipLevel = 0;
info.subresourceRange.levelCount = 1;
info.subresourceRange.baseArrayLayer = 0;
info.subresourceRange.layerCount = 1;
return Device::getInstance()->createImageView( info );
}
ImageView::ImageView( std::shared_ptr< ImageHandle >& img ) :
m_resource( img ),
m_descriptor_info(),
m_image_view( VK_NULL_HANDLE ),
m_sampler()
m_sampler(),
m_image_view( createImageView( img ) )
{
vk::ImageViewCreateInfo view_info {};
view_info.image = img->getVkImage();
view_info.viewType = vk::ImageViewType::e2D;
view_info.format = img->format();
view_info.subresourceRange.aspectMask = img->aspectMask();
view_info.subresourceRange.baseMipLevel = 0;
view_info.subresourceRange.levelCount = 1;
view_info.subresourceRange.baseArrayLayer = 0;
view_info.subresourceRange.layerCount = 1;
m_descriptor_info.imageLayout = img->m_final_layout;
m_descriptor_info.imageView = m_image_view;
if ( Device::getInstance().device().createImageView( &view_info, nullptr, &m_image_view )
!= vk::Result::eSuccess )
throw std::runtime_error( "Failed to create image view" );
}
vk::DescriptorImageInfo ImageView::descriptorInfo( vk::Sampler sampler, vk::ImageLayout layout ) const
@@ -51,16 +52,11 @@ namespace fgl::engine
return info;
}
vk::ImageView& ImageView::getVkView()
vk::raii::ImageView& ImageView::getVkView()
{
return m_image_view;
}
vk::Image& ImageView::getVkImage()
{
return m_resource->getVkImage();
}
vk::Extent2D ImageView::getExtent() const
{
return m_resource->extent();

View File

@@ -6,12 +6,9 @@
#include <vulkan/vulkan.hpp>
#include <optional>
#include "Image.hpp"
#include "ImageHandle.hpp"
#include "Sampler.hpp"
#include "engine/concepts/is_image.hpp"
#include "engine/rendering/Device.hpp"
namespace fgl::engine
@@ -23,7 +20,7 @@ namespace fgl::engine
vk::DescriptorImageInfo m_descriptor_info;
vk::ImageView m_image_view;
vk::raii::ImageView m_image_view;
Sampler m_sampler;
@@ -48,21 +45,16 @@ namespace fgl::engine
other.m_image_view = VK_NULL_HANDLE;
}
~ImageView()
{
if ( m_image_view )
{
Device::getInstance().device().destroyImageView( m_image_view );
}
}
vk::raii::ImageView createImageView( const std::shared_ptr< ImageHandle >& img );
ImageView( std::shared_ptr< ImageHandle >& img );
vk::Extent2D getExtent() const;
vk::ImageView& getVkView();
vk::raii::ImageView& getVkView();
vk::Image& getVkImage();
VkImageView operator*() { return *m_image_view; }
VkImage getVkImage() { return m_resource->getVkImage(); }
Sampler& getSampler() { return m_sampler; };

View File

@@ -11,35 +11,40 @@
namespace fgl::engine
{
Sampler::Sampler(
vk::raii::Sampler createSampler(
vk::Filter min_filter,
vk::Filter mag_filter,
vk::SamplerMipmapMode mipmap_mode,
vk::SamplerAddressMode sampler_mode ) :
valid( true )
vk::SamplerMipmapMode mipmode,
vk::SamplerAddressMode address_mode )
{
vk::SamplerCreateInfo info;
info.magFilter = mag_filter;
info.minFilter = min_filter;
info.mipmapMode = mipmap_mode;
info.mipmapMode = mipmode;
info.addressModeU = sampler_mode;
info.addressModeV = sampler_mode;
info.addressModeW = sampler_mode;
info.addressModeU = address_mode;
info.addressModeV = address_mode;
info.addressModeW = address_mode;
info.minLod = -1000;
info.maxLod = 1000;
info.maxAnisotropy = 1.0f;
if ( Device::getInstance().device().createSampler( &info, nullptr, &m_sampler ) != vk::Result::eSuccess )
{
throw std::runtime_error( "Failed to create sampler" );
}
return Device::getInstance()->createSampler( info );
}
Sampler::Sampler(
vk::Filter min_filter,
vk::Filter mag_filter,
vk::SamplerMipmapMode mipmap_mode,
vk::SamplerAddressMode sampler_mode ) :
valid( true ),
m_sampler( createSampler( mag_filter, min_filter, mipmap_mode, sampler_mode ) )
{}
Sampler::Sampler( Sampler&& other ) : valid( other.valid ), m_sampler( std::move( other.m_sampler ) )
{
other.valid = false;
@@ -53,9 +58,4 @@ namespace fgl::engine
return *this;
}
Sampler::~Sampler()
{
if ( valid ) Device::getInstance().device().destroySampler( m_sampler );
}
} // namespace fgl::engine

View File

@@ -5,14 +5,15 @@
#pragma once
#include <vulkan/vulkan.hpp>
#include <vulkan/vulkan_raii.hpp>
namespace fgl::engine
{
class Sampler
{
bool valid { false };
vk::Sampler m_sampler { VK_NULL_HANDLE };
bool valid;
vk::raii::Sampler m_sampler;
public:
@@ -30,14 +31,14 @@ namespace fgl::engine
vk::SamplerMipmapMode mipmap_mode,
vk::SamplerAddressMode sampler_mode );
VkSampler operator*() { return *m_sampler; }
Sampler( const Sampler& ) = delete;
Sampler& operator=( const Sampler& ) = delete;
Sampler( Sampler&& other );
Sampler& operator=( Sampler&& );
~Sampler();
vk::Sampler& getVkSampler() { return m_sampler; }
vk::raii::Sampler& getVkSampler() { return m_sampler; }
};
} // namespace fgl::engine

View File

@@ -133,7 +133,7 @@ namespace fgl::engine
return model_ptr;
}
void Model::stage( vk::CommandBuffer& cmd_buffer )
void Model::stage( vk::raii::CommandBuffer& cmd_buffer )
{
assert( !m_primitives.empty() );
for ( auto& primitive : m_primitives )

View File

@@ -64,7 +64,7 @@ namespace fgl::engine
static std::vector< std::shared_ptr< Model > > createModelsFromScene(
Device& device, const std::filesystem::path& path, Buffer& vertex_buffer, Buffer& index_buffer );
void stage( vk::CommandBuffer& cmd_buffer );
void stage( vk::raii::CommandBuffer& cmd_buffer );
const std::string& getName() const { return m_name; }

View File

@@ -4,7 +4,6 @@
#include "Pipeline.hpp"
#include <array>
#include <fstream>
#include "Shader.hpp"
@@ -13,8 +12,10 @@
namespace fgl::engine::internal
{
void Pipeline::createGraphicsPipeline(
std::vector< std::unique_ptr< ShaderHandle > >& shaders, const PipelineConfigInfo& info )
vk::raii::Pipeline Pipeline::createGraphicsPipeline(
std::vector< std::unique_ptr< ShaderHandle > >& shaders,
const PipelineConfigInfo& info,
vk::raii::PipelineLayout& layout )
{
assert( info.render_pass != VK_NULL_HANDLE && "Cannot create graphics pipeline: no render pass provided" );
@@ -52,26 +53,16 @@ namespace fgl::engine::internal
pipeline_info.pDepthStencilState = &info.depth_stencil_info;
pipeline_info.pColorBlendState = &info.color_blend_info;
pipeline_info.pDynamicState = &info.dynamic_state_info;
pipeline_info.layout = info.layout;
pipeline_info.layout = *layout;
pipeline_info.renderPass = info.render_pass;
pipeline_info.subpass = info.subpass;
pipeline_info.basePipelineHandle = VK_NULL_HANDLE;
pipeline_info.basePipelineIndex = -1;
if ( auto temp = m_device.device().createGraphicsPipeline( VK_NULL_HANDLE, pipeline_info );
temp.result != vk::Result::eSuccess )
throw std::runtime_error( "Failed to create graphics pipeline" );
else
m_vk_pipeline = temp.value;
return m_device->createGraphicsPipeline( VK_NULL_HANDLE, pipeline_info );
}
Pipeline::~Pipeline()
{
m_device.device().destroyPipelineLayout( m_layout, nullptr );
m_device.device().destroyPipeline( m_vk_pipeline, nullptr );
}
void Pipeline::bind( vk::CommandBuffer& command_buffer )
void Pipeline::bind( vk::raii::CommandBuffer& command_buffer )
{
command_buffer.bindPipeline( vk::PipelineBindPoint::eGraphics, m_vk_pipeline );
}
@@ -81,7 +72,7 @@ namespace fgl::engine::internal
vk::DebugUtilsObjectNameInfoEXT info {};
info.objectType = vk::ObjectType::ePipeline;
info.pObjectName = str.c_str();
info.objectHandle = reinterpret_cast< std::uint64_t >( static_cast< VkPipeline >( this->m_vk_pipeline ) );
info.objectHandle = reinterpret_cast< std::uint64_t >( static_cast< VkPipeline >( *this->m_vk_pipeline ) );
Device::getInstance().setDebugUtilsObjectName( info );
}

View File

@@ -23,24 +23,32 @@ namespace fgl::engine::internal
protected:
Device& m_device;
vk::Pipeline m_vk_pipeline { VK_NULL_HANDLE };
vk::PipelineLayout m_layout { VK_NULL_HANDLE };
vk::raii::PipelineLayout m_layout;
vk::raii::Pipeline m_vk_pipeline;
vk::ShaderModule m_vert_shader { VK_NULL_HANDLE };
vk::ShaderModule m_frag_shader { VK_NULL_HANDLE };
void createGraphicsPipeline(
std::vector< std::unique_ptr< ShaderHandle > >& shaders, const PipelineConfigInfo& info );
vk::raii::Pipeline createGraphicsPipeline(
std::vector< std::unique_ptr< ShaderHandle > >& shaders,
const PipelineConfigInfo& info,
vk::raii::PipelineLayout& layout );
public:
Pipeline( Device& device ) : m_device( device ) {}
~Pipeline();
Pipeline(
Device& device,
vk::raii::PipelineLayout layout,
PipelineConfigInfo info,
std::vector< std::unique_ptr< ShaderHandle > > shaders ) :
m_device( device ),
m_layout( std::move( layout ) ),
m_vk_pipeline( createGraphicsPipeline( shaders, info, m_layout ) )
{}
Pipeline( const Pipeline& other ) = delete;
Pipeline& operator=( const Pipeline& ) = delete;
void bind( vk::CommandBuffer& command_buffer );
void bind( vk::raii::CommandBuffer& command_buffer );
void setDebugName( const std::string str );
};

View File

@@ -127,7 +127,7 @@ namespace fgl::engine
info.rasterization_info.cullMode = vk::CullModeFlagBits::eNone;
}
PipelineConfigInfo::PipelineConfigInfo( vk::RenderPass pass )
PipelineConfigInfo::PipelineConfigInfo( vk::raii::RenderPass& pass )
{
render_pass = pass;
defaultConfig( *this );

View File

@@ -5,10 +5,13 @@
#pragma once
#include <vulkan/vulkan.hpp>
#include <vulkan/vulkan_raii.hpp>
#include <cstdint>
#include <vector>
#include "engine/FGL_DEFINES.hpp"
namespace fgl::engine
{
@@ -27,16 +30,18 @@ namespace fgl::engine
std::vector< vk::DynamicState > dynamic_state_enables {};
vk::PipelineDynamicStateCreateInfo dynamic_state_info {};
vk::PipelineLayout layout { nullptr };
vk::RenderPass render_pass { nullptr };
vk::RenderPass render_pass { VK_NULL_HANDLE };
std::uint32_t subpass { 0 };
std::vector< vk::VertexInputBindingDescription > binding_descriptions {};
std::vector< vk::VertexInputAttributeDescription > attribute_descriptions {};
PipelineConfigInfo( vk::RenderPass pass );
PipelineConfigInfo( const PipelineConfigInfo& other ) = delete;
PipelineConfigInfo& operator=( const PipelineConfigInfo& ) = delete;
FGL_DELETE_COPY( PipelineConfigInfo )
PipelineConfigInfo( vk::raii::RenderPass& pass );
PipelineConfigInfo& operator=( PipelineConfigInfo&& other ) = default;
PipelineConfigInfo( PipelineConfigInfo&& other ) = default;
static void disableVertexInput( PipelineConfigInfo& info );
static void setTriangleListTopo( PipelineConfigInfo& info );

View File

@@ -83,44 +83,38 @@ namespace fgl::engine
return emptySetsBeforeIDX< start_idx - 1 >() + is_empty_descriptor_set< BindingSet< start_idx > >;
}
vk::PipelineLayout createLayout( Device& device )
vk::raii::PipelineLayout createLayout( [[maybe_unused]] Device& device )
{
if ( m_layout != VK_NULL_HANDLE ) return m_layout;
std::vector< vk::raii::DescriptorSetLayout > layouts { DescriptorSetCollection::createDescriptorSets() };
std::vector< vk::DescriptorSetLayout > vk_layouts {};
vk_layouts.reserve( layouts.size() );
typename DescriptorSetCollection::LayoutArray layouts { DescriptorSetCollection::createDescriptorSets() };
for ( vk::raii::DescriptorSetLayout& layout : layouts )
{
vk_layouts.emplace_back( *layout );
}
vk::PipelineLayoutCreateInfo pipeline_layout_info {};
pipeline_layout_info.setLayoutCount = has_binding_sets ? static_cast< uint32_t >( layouts.size() ) : 0;
pipeline_layout_info.pSetLayouts = has_binding_sets ? layouts.data() : VK_NULL_HANDLE;
pipeline_layout_info.setLayoutCount = has_binding_sets ? static_cast< uint32_t >( vk_layouts.size() ) : 0;
pipeline_layout_info.pSetLayouts = has_binding_sets ? vk_layouts.data() : VK_NULL_HANDLE;
pipeline_layout_info.pushConstantRangeCount = has_constant_range ? 1 : 0;
pipeline_layout_info.pPushConstantRanges = has_constant_range ? getRange() : VK_NULL_HANDLE;
if ( device.device().createPipelineLayout( &pipeline_layout_info, nullptr, &m_layout )
!= vk::Result::eSuccess )
throw std::runtime_error( "Failed to create pipeline layout" );
return m_layout;
}
PipelineConfigInfo& populate( PipelineConfigInfo& info, Device& device )
{
info.layout = createLayout( device );
return info;
return Device::getInstance()->createPipelineLayout( pipeline_layout_info );
}
public:
vk::PipelineLayout getLayout() { return m_layout; }
void bindDescriptor( vk::CommandBuffer cmd_buffer, std::uint16_t set_idx, DescriptorSet& descriptor )
void bindDescriptor( vk::raii::CommandBuffer& cmd_buffer, std::uint16_t set_idx, DescriptorSet& descriptor )
{
cmd_buffer.bindDescriptorSets(
vk::PipelineBindPoint::eGraphics, m_layout, set_idx, 1, &( descriptor.getSet() ), 0, nullptr );
const std::vector< vk::DescriptorSet > sets { *descriptor };
const std::vector< std::uint32_t > offsets {};
cmd_buffer.bindDescriptorSets( vk::PipelineBindPoint::eGraphics, m_layout, set_idx, sets, offsets );
}
template < typename TPushData >
void pushConstant( vk::CommandBuffer cmd_buffer, TPushData& data )
void pushConstant( vk::raii::CommandBuffer& cmd_buffer, TPushData& data )
{
if constexpr ( has_constant_range )
{
@@ -135,14 +129,13 @@ namespace fgl::engine
assert( "Attempted to push constant to pipeline without push constant range" );
}
PipelineT( Device& device, PipelineConfigInfo& info ) : Pipeline( device )
{
populate( info, device );
auto shaders { ShaderCollection::loadShaders() };
createGraphicsPipeline( shaders, info );
}
PipelineT( Device& device, PipelineConfigInfo&& info ) :
Pipeline(
device,
createLayout( device ),
std::forward< PipelineConfigInfo >( info ),
ShaderCollection::loadShaders() )
{}
};
} // namespace fgl::engine

View File

@@ -0,0 +1,52 @@
//
// Created by kj16609 on 6/21/24.
//
#include "Shader.hpp"
#include "engine/rendering/Device.hpp"
namespace fgl::engine
{
std::vector< std::byte > ShaderHandle::loadData( const std::filesystem::path& path )
{
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() );
return data;
}
else
{
log::critical( "Failed to load shader module {}. Path not found", path.string() );
throw std::runtime_error( "Failed to load shader module. Path not found" );
}
}
vk::ShaderModuleCreateInfo ShaderHandle::createModuleInfo()
{
vk::ShaderModuleCreateInfo module_info {};
module_info.flags = {};
module_info.codeSize = shader_data.size();
module_info.pCode = reinterpret_cast< const std::uint32_t* >( shader_data.data() );
return module_info;
}
ShaderHandle::ShaderHandle( const std::filesystem::path path, const vk::PipelineShaderStageCreateInfo info ) :
shader_data( loadData( path ) ),
module_create_info( createModuleInfo() ),
shader_module( Device::getInstance()->createShaderModule( module_create_info ) ),
stage_info( info )
{
stage_info.module = shader_module;
}
} // namespace fgl::engine

View File

@@ -4,6 +4,8 @@
#pragma once
#include <vulkan/vulkan_raii.hpp>
#include <fstream>
#include "engine/logging/logging.hpp"
@@ -31,41 +33,16 @@ namespace fgl::engine
struct ShaderHandle
{
std::vector< std::byte > shader_data;
vk::ShaderModuleCreateInfo module_create_info;
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 );
vk::raii::ShaderModule shader_module;
static_assert( sizeof( std::ifstream::char_type ) == sizeof( std::byte ) );
ifs.read( reinterpret_cast< std::ifstream::char_type* >( data.data() ), data.size() );
std::vector< std::byte > loadData( const std::filesystem::path& );
vk::ShaderModuleCreateInfo createModuleInfo();
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
{
log::critical( "Failed to load shader module {}. Path not found", path.string() );
throw std::runtime_error( "Failed to load shader module. Path not found" );
}
}
ShaderHandle( const std::filesystem::path path, const vk::PipelineShaderStageCreateInfo info );
ShaderHandle( const ShaderHandle& other ) = delete;
@@ -74,11 +51,6 @@ namespace fgl::engine
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 );

View File

@@ -21,65 +21,62 @@ namespace fgl::engine
return *global_device;
}
// class member functions
Device::Device( Window& window, Instance& instance ) : m_instance( instance )
vk::PhysicalDeviceFeatures Device::DeviceCreateInfo::getDeviceFeatures( PhysicalDevice& physical_device )
{
assert( !global_device );
const vk::PhysicalDeviceFeatures available_features { physical_device->getFeatures() };
createSurface( window );
pickPhysicalDevice();
createLogicalDevice();
createVMAAllocator();
createCommandPool();
if ( available_features.samplerAnisotropy != VK_TRUE )
{
throw std::runtime_error( "samplerAnsitrophy not supported by device" );
}
global_device = this;
if ( available_features.multiDrawIndirect != VK_TRUE )
{
throw std::runtime_error( "multiDrawIndirect not supported by device" );
}
DescriptorPool::init( *global_device );
if ( available_features.tessellationShader != VK_TRUE )
{
throw std::runtime_error( "Tesselation shader not supported by device" );
}
if ( available_features.drawIndirectFirstInstance != VK_TRUE )
{
throw std::runtime_error( "drawIndirectFirstInstance not supported by device" );
}
//Set enabled features
vk::PhysicalDeviceFeatures deviceFeatures = {};
deviceFeatures.samplerAnisotropy = VK_TRUE;
deviceFeatures.multiDrawIndirect = VK_TRUE;
deviceFeatures.tessellationShader = VK_TRUE;
deviceFeatures.drawIndirectFirstInstance = VK_TRUE;
return deviceFeatures;
}
Device::~Device()
vk::PhysicalDeviceDescriptorIndexingFeatures Device::DeviceCreateInfo::getIndexingFeatures()
{
vkDestroyCommandPool( m_device, m_commandPool, nullptr );
vkDestroyDevice( m_device, nullptr );
vk::PhysicalDeviceDescriptorIndexingFeatures indexing_features {};
indexing_features.setRuntimeDescriptorArray( VK_TRUE );
indexing_features.setDescriptorBindingPartiallyBound( VK_TRUE );
indexing_features.setShaderSampledImageArrayNonUniformIndexing( VK_TRUE );
indexing_features.setDescriptorBindingSampledImageUpdateAfterBind( VK_TRUE );
vkDestroySurfaceKHR( m_instance, m_surface_khr, nullptr );
vkDestroyInstance( m_instance, nullptr );
return indexing_features;
}
void Device::pickPhysicalDevice()
std::vector< vk::DeviceQueueCreateInfo > Device::DeviceCreateInfo::getQueueCreateInfos( PhysicalDevice&
physical_device )
{
std::vector< vk::PhysicalDevice > devices {
static_cast< vk::Instance >( m_instance ).enumeratePhysicalDevices()
std::vector< vk::DeviceQueueCreateInfo > queueCreateInfos;
std::set< std::uint32_t > uniqueQueueFamilies = {
physical_device.queueInfo().getIndex( vk::QueueFlagBits::eGraphics ),
physical_device.queueInfo().getPresentIndex(),
};
bool found { false };
for ( const auto& device : devices )
{
if ( isDeviceSuitable( device ) )
{
m_physical_device = device;
found = true;
break;
}
}
m_properties = m_physical_device.getProperties();
if ( !found )
{
throw std::runtime_error( "failed to find a suitable GPU!" );
}
}
void Device::createLogicalDevice()
{
const QueueFamilyIndices indices { findQueueFamilies( m_physical_device ) };
std::vector< vk::DeviceQueueCreateInfo > queueCreateInfos;
std::set< uint32_t > uniqueQueueFamilies = { indices.graphicsFamily, indices.presentFamily };
float queuePriority = 1.0f;
//TODO: Store this somewhere where it doesn't need to be static
static float queuePriority = 1.0f;
for ( uint32_t queueFamily : uniqueQueueFamilies )
{
vk::DeviceQueueCreateInfo queueCreateInfo = {};
@@ -89,30 +86,23 @@ namespace fgl::engine
queueCreateInfos.push_back( queueCreateInfo );
}
vk::PhysicalDeviceFeatures deviceFeatures = {};
deviceFeatures.samplerAnisotropy = VK_TRUE;
deviceFeatures.multiDrawIndirect = VK_TRUE;
deviceFeatures.tessellationShader = VK_TRUE;
deviceFeatures.drawIndirectFirstInstance = VK_TRUE;
vk::PhysicalDeviceDescriptorIndexingFeatures indexing_features {};
indexing_features.setRuntimeDescriptorArray( true );
indexing_features.setDescriptorBindingPartiallyBound( true );
indexing_features.setShaderSampledImageArrayNonUniformIndexing( true );
indexing_features.setDescriptorBindingSampledImageUpdateAfterBind( true );
return queueCreateInfos;
}
vk::DeviceCreateInfo Device::DeviceCreateInfo::getCreateInfo( PhysicalDevice& physical_device )
{
vk::DeviceCreateInfo createInfo {};
createInfo.queueCreateInfoCount = static_cast< uint32_t >( queueCreateInfos.size() );
createInfo.pQueueCreateInfos = queueCreateInfos.data();
createInfo.queueCreateInfoCount = static_cast< uint32_t >( m_queue_create_infos.size() );
createInfo.pQueueCreateInfos = m_queue_create_infos.data();
createInfo.pEnabledFeatures = &deviceFeatures;
createInfo.pEnabledFeatures = &m_requested_features;
createInfo.enabledExtensionCount = static_cast< uint32_t >( deviceExtensions.size() );
createInfo.ppEnabledExtensionNames = deviceExtensions.data();
createInfo.setPNext( &indexing_features );
createInfo.setPNext( &m_indexing_features );
//Get device extension list
const auto supported_extensions { m_physical_device.enumerateDeviceExtensionProperties() };
const auto supported_extensions { physical_device.handle().enumerateDeviceExtensionProperties() };
std::cout << "Supported device extensions:" << std::endl;
for ( auto& desired_ext : deviceExtensions )
{
@@ -131,7 +121,7 @@ namespace fgl::engine
// might not really be necessary anymore because device specific validation layers
// have been deprecated
if ( enableValidationLayers )
if ( true )
{
createInfo.enabledLayerCount = static_cast< uint32_t >( validationLayers.size() );
createInfo.ppEnabledLayerNames = validationLayers.data();
@@ -141,59 +131,47 @@ namespace fgl::engine
createInfo.enabledLayerCount = 0;
}
if ( auto res = m_physical_device.createDevice( &createInfo, nullptr, &m_device ); res != vk::Result::eSuccess )
{
throw std::runtime_error( "failed to create logical device!" );
}
m_device.getQueue( indices.graphicsFamily, 0, &m_graphics_queue );
m_device.getQueue( indices.presentFamily, 0, &m_present_queue );
return createInfo;
}
void Device::createCommandPool()
{
QueueFamilyIndices queueFamilyIndices = findPhysicalQueueFamilies();
Device::DeviceCreateInfo::DeviceCreateInfo( PhysicalDevice& physical_device ) :
m_requested_features( getDeviceFeatures( physical_device ) ),
m_indexing_features( getIndexingFeatures() ),
m_queue_create_infos( getQueueCreateInfos( physical_device ) ),
m_create_info( getCreateInfo( physical_device ) )
{}
vk::CommandPoolCreateInfo Device::commandPoolInfo()
{
vk::CommandPoolCreateInfo poolInfo = {};
poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily;
poolInfo.queueFamilyIndex = m_physical_device.queueInfo().getIndex( vk::QueueFlagBits::eGraphics );
poolInfo.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer | vk::CommandPoolCreateFlagBits::eTransient;
if ( m_device.createCommandPool( &poolInfo, nullptr, &m_commandPool ) != vk::Result::eSuccess )
{
throw std::runtime_error( "failed to create command pool!" );
}
return poolInfo;
}
void Device::createSurface( Window& window )
// class member functions
Device::Device( Window& window, Instance& instance ) :
m_instance( instance ),
m_surface_khr( window, instance ),
m_physical_device( m_instance, m_surface_khr ),
device_creation_info( m_physical_device ),
m_device( m_physical_device, device_creation_info.m_create_info ),
m_commandPool( m_device.createCommandPool( commandPoolInfo() ) ),
m_graphics_queue( m_device
.getQueue( m_physical_device.queueInfo().getIndex( vk::QueueFlagBits::eGraphics ), 0 ) ),
m_present_queue( m_device.getQueue( m_physical_device.queueInfo().getPresentIndex(), 0 ) ),
m_allocator( createVMAAllocator() )
{
m_surface_khr = window.createWindowSurface( m_instance );
assert( !global_device );
global_device = this;
DescriptorPool::init( *global_device );
}
bool Device::isDeviceSuitable( vk::PhysicalDevice device )
{
const QueueFamilyIndices indices { findQueueFamilies( device ) };
const bool extensionsSupported { checkDeviceExtensionSupport( device ) };
bool swapChainAdequate { false };
if ( extensionsSupported )
{
const SwapChainSupportDetails swapChainSupport { querySwapChainSupport( device ) };
swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
}
vk::PhysicalDeviceFeatures supportedFeatures;
device.getFeatures( &supportedFeatures );
std::cout << "Device: " << device.getProperties().deviceName << std::endl;
std::cout << "\tgraphicsFamily: " << indices.graphicsFamily << std::endl;
std::cout << "\tpresentFamily: " << indices.presentFamily << std::endl;
std::cout << "\textensionsSupported: " << extensionsSupported << std::endl;
std::cout << "\tswapChainAdequate: " << swapChainAdequate << std::endl;
std::cout << "\tsamplerAnisotropy: " << supportedFeatures.samplerAnisotropy << std::endl;
return indices.isComplete() && extensionsSupported && swapChainAdequate && supportedFeatures.samplerAnisotropy;
}
Device::~Device()
{}
bool Device::checkValidationLayerSupport()
{
@@ -221,28 +199,7 @@ namespace fgl::engine
return true;
}
std::vector< const char* > Device::getRequiredInstanceExtensions()
{
uint32_t glfwExtensionCount = 0;
const char** glfwExtensions;
glfwExtensions = glfwGetRequiredInstanceExtensions( &glfwExtensionCount );
if ( glfwExtensions == nullptr ) throw std::runtime_error( "Failed to get required extensions from glfw" );
std::vector< const char* > extensions( glfwExtensions, glfwExtensions + glfwExtensionCount );
// "VK_KHR_surface" is guaranteed to be in this list
assert( extensions.size() >= 1 );
if ( enableValidationLayers )
{
extensions.push_back( VK_EXT_DEBUG_UTILS_EXTENSION_NAME );
}
return extensions;
}
bool Device::checkDeviceExtensionSupport( vk::PhysicalDevice device )
bool Device::checkDeviceExtensionSupport( vk::raii::PhysicalDevice device )
{
const std::vector< vk::ExtensionProperties > availableExtensions {
device.enumerateDeviceExtensionProperties()
@@ -267,68 +224,14 @@ namespace fgl::engine
return found_count == required_count;
}
QueueFamilyIndices Device::findQueueFamilies( vk::PhysicalDevice device )
{
QueueFamilyIndices indices {};
std::vector< vk::QueueFamilyProperties > queueFamilies { device.getQueueFamilyProperties() };
int i { 0 };
for ( const auto& queueFamily : queueFamilies )
{
if ( queueFamily.queueCount > 0 && queueFamily.queueFlags & vk::QueueFlagBits::eGraphics )
{
indices.graphicsFamily = i;
indices.graphicsFamilyHasValue = true;
}
vk::Bool32 presentSupport { VK_FALSE };
vkGetPhysicalDeviceSurfaceSupportKHR( device, i, m_surface_khr, &presentSupport );
if ( queueFamily.queueCount > 0 && presentSupport )
{
indices.presentFamily = i;
indices.presentFamilyHasValue = true;
}
if ( indices.isComplete() )
{
break;
}
i++;
}
return indices;
}
SwapChainSupportDetails Device::querySwapChainSupport( vk::PhysicalDevice device )
SwapChainSupportDetails Device::querySwapChainSupport( vk::raii::PhysicalDevice device )
{
SwapChainSupportDetails details;
if ( device.getSurfaceCapabilitiesKHR( m_surface_khr, &details.capabilities ) != vk::Result::eSuccess )
throw std::runtime_error( "failed to get surface capabilities" );
details.capabilities = device.getSurfaceCapabilitiesKHR( m_surface_khr );
details.formats = device.getSurfaceFormatsKHR( m_surface_khr );
details.presentModes = device.getSurfacePresentModesKHR( m_surface_khr );
uint32_t formatCount { 0 };
if ( device.getSurfaceFormatsKHR( m_surface_khr, &formatCount, nullptr ) != vk::Result::eSuccess )
throw std::runtime_error( "failed to get surface formats" );
if ( formatCount != 0 )
{
details.formats.resize( formatCount );
if ( device.getSurfaceFormatsKHR( m_surface_khr, &formatCount, details.formats.data() )
!= vk::Result::eSuccess )
throw std::runtime_error( "failed to get surface formats" );
}
uint32_t presentModeCount { 0 };
if ( device.getSurfacePresentModesKHR( m_surface_khr, &presentModeCount, nullptr ) != vk::Result::eSuccess )
throw std::runtime_error( "failed to get surface present modes" );
if ( presentModeCount != 0 )
{
details.presentModes.resize( presentModeCount );
if ( device.getSurfacePresentModesKHR( m_surface_khr, &presentModeCount, details.presentModes.data() )
!= vk::Result::eSuccess )
throw std::runtime_error( "failed to get surface present modes" );
}
return details;
}
@@ -337,8 +240,7 @@ namespace fgl::engine
{
for ( vk::Format format : candidates )
{
vk::FormatProperties props;
m_physical_device.getFormatProperties( format, &props );
vk::FormatProperties props { m_physical_device.handle().getFormatProperties( format ) };
if ( tiling == vk::ImageTiling::eLinear && ( props.linearTilingFeatures & features ) == features )
{
@@ -354,8 +256,7 @@ namespace fgl::engine
uint32_t Device::findMemoryType( uint32_t typeFilter, vk::MemoryPropertyFlags properties )
{
vk::PhysicalDeviceMemoryProperties memProperties;
m_physical_device.getMemoryProperties( &memProperties );
vk::PhysicalDeviceMemoryProperties memProperties { m_physical_device.handle().getMemoryProperties() };
for ( uint32_t i = 0; i < memProperties.memoryTypeCount; i++ )
{
if ( ( typeFilter & ( 1 << i ) )
@@ -368,7 +269,7 @@ namespace fgl::engine
throw std::runtime_error( "failed to find suitable memory type!" );
}
vk::CommandBuffer Device::beginSingleTimeCommands()
vk::raii::CommandBuffer Device::beginSingleTimeCommands()
{
ZoneScoped;
vk::CommandBufferAllocateInfo allocInfo {};
@@ -376,40 +277,43 @@ namespace fgl::engine
allocInfo.commandPool = m_commandPool;
allocInfo.commandBufferCount = 1;
vk::CommandBuffer commandBuffer {};
if ( m_device.allocateCommandBuffers( &allocInfo, &commandBuffer ) != vk::Result::eSuccess )
throw std::runtime_error( "failed to allocate command buffers!" );
auto command_buffers { m_device.allocateCommandBuffers( allocInfo ) };
assert( command_buffers.size() == 1 );
vk::raii::CommandBuffer command_buffer { std::move( command_buffers[ 0 ] ) };
vk::CommandBufferBeginInfo beginInfo {};
beginInfo.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;
commandBuffer.begin( beginInfo );
command_buffer.begin( beginInfo );
return commandBuffer;
return command_buffer;
}
void Device::endSingleTimeCommands( vk::CommandBuffer commandBuffer )
void Device::endSingleTimeCommands( vk::raii::CommandBuffer& commandBuffer )
{
ZoneScoped;
vkEndCommandBuffer( commandBuffer );
commandBuffer.end();
vk::SubmitInfo submitInfo {};
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffer;
submitInfo.pCommandBuffers = &( *commandBuffer );
if ( m_graphics_queue.submit( 1, &submitInfo, VK_NULL_HANDLE ) != vk::Result::eSuccess )
throw std::runtime_error( "failed to submit single time command buffer!" );
std::vector< vk::SubmitInfo > submit_infos { submitInfo };
m_graphics_queue.submit( submit_infos );
m_graphics_queue.waitIdle();
m_device.freeCommandBuffers( m_commandPool, 1, &commandBuffer );
//m_device.freeCommandBuffers( m_commandPool, 1, &commandBuffer );
}
void Device::
copyBufferToImage( vk::Buffer buffer, vk::Image image, uint32_t width, uint32_t height, uint32_t layerCount )
{
ZoneScoped;
vk::CommandBuffer commandBuffer { beginSingleTimeCommands() };
vk::raii::CommandBuffer commandBuffer { beginSingleTimeCommands() };
vk::BufferImageCopy region {};
region.bufferOffset = 0;
@@ -424,50 +328,55 @@ namespace fgl::engine
region.imageOffset = vk::Offset3D { 0, 0, 0 };
region.imageExtent = vk::Extent3D { width, height, 1 };
commandBuffer.copyBufferToImage( buffer, image, vk::ImageLayout::eTransferDstOptimal, 1, &region );
std::vector< vk::BufferImageCopy > regions { region };
commandBuffer.copyBufferToImage( buffer, image, vk::ImageLayout::eTransferDstOptimal, regions );
endSingleTimeCommands( commandBuffer );
}
void Device::createVMAAllocator()
VmaAllocator Device::createVMAAllocator()
{
VmaVulkanFunctions vk_func {};
vk_func.vkGetInstanceProcAddr = vkGetInstanceProcAddr;
vk_func.vkGetDeviceProcAddr = vkGetDeviceProcAddr;
VmaAllocatorCreateInfo create_info {};
create_info.physicalDevice = m_physical_device;
create_info.device = m_device;
create_info.physicalDevice = *m_physical_device.handle();
create_info.device = *m_device;
create_info.pVulkanFunctions = &vk_func;
create_info.instance = m_instance;
create_info.vulkanApiVersion = VK_API_VERSION_1_0;
if ( vmaCreateAllocator( &create_info, &m_allocator ) != VK_SUCCESS )
VmaAllocator allocator;
if ( vmaCreateAllocator( &create_info, &allocator ) != VK_SUCCESS )
throw std::runtime_error( "Failed to create VMA allocator" );
return allocator;
}
void Device::copyBuffer(
vk::Buffer dst, vk::Buffer src, vk::DeviceSize dst_offset, vk::DeviceSize src_offset, vk::DeviceSize size )
{
vk::CommandBuffer commandBuffer { beginSingleTimeCommands() };
vk::raii::CommandBuffer commandBuffer { beginSingleTimeCommands() };
vk::BufferCopy copyRegion {};
copyRegion.size = size;
copyRegion.srcOffset = src_offset;
copyRegion.dstOffset = dst_offset;
commandBuffer.copyBuffer( src, dst, 1, &copyRegion );
std::vector< vk::BufferCopy > copy_regions { copyRegion };
commandBuffer.copyBuffer( src, dst, copy_regions );
endSingleTimeCommands( commandBuffer );
}
vk::Result Device::setDebugUtilsObjectName( [[maybe_unused]] const vk::DebugUtilsObjectNameInfoEXT& nameInfo )
vk::Result Device::setDebugUtilsObjectName( const vk::DebugUtilsObjectNameInfoEXT& nameInfo )
{
#ifndef NDEBUG
if ( device().setDebugUtilsObjectNameEXT( &nameInfo ) != vk::Result::eSuccess )
{
std::cout << "Failed to set debug object name" << std::endl;
throw std::runtime_error( "Failed to set debug object name" );
}
device().setDebugUtilsObjectNameEXT( nameInfo );
#endif
return vk::Result::eSuccess;
}

View File

@@ -7,6 +7,7 @@
#include <vector>
#include "Instance.hpp"
#include "PhysicalDevice.hpp"
#include "engine/Window.hpp"
#include "engine/concepts/is_suballocation.hpp"
#include "vma/vma_impl.hpp"
@@ -35,19 +36,38 @@ namespace fgl::engine
{
Instance& m_instance;
vk::PhysicalDevice m_physical_device { VK_NULL_HANDLE };
vk::CommandPool m_commandPool { VK_NULL_HANDLE };
Surface m_surface_khr;
VmaAllocator m_allocator { VK_NULL_HANDLE };
PhysicalDevice m_physical_device;
vk::Device m_device { VK_NULL_HANDLE };
vk::SurfaceKHR m_surface_khr { VK_NULL_HANDLE };
vk::Queue m_graphics_queue { VK_NULL_HANDLE };
vk::Queue m_present_queue { VK_NULL_HANDLE };
inline static std::vector< const char* > validationLayers { "VK_LAYER_KHRONOS_validation" };
inline static std::vector< const char* > deviceExtensions { VK_KHR_SWAPCHAIN_EXTENSION_NAME,
VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME };
std::vector< const char* > validationLayers { "VK_LAYER_KHRONOS_validation" };
std::vector< const char* > deviceExtensions { VK_KHR_SWAPCHAIN_EXTENSION_NAME,
VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME };
struct DeviceCreateInfo
{
vk::PhysicalDeviceFeatures m_requested_features;
vk::PhysicalDeviceDescriptorIndexingFeatures m_indexing_features;
std::vector< vk::DeviceQueueCreateInfo > m_queue_create_infos;
vk::DeviceCreateInfo m_create_info;
vk::PhysicalDeviceFeatures getDeviceFeatures( PhysicalDevice& );
vk::PhysicalDeviceDescriptorIndexingFeatures getIndexingFeatures();
std::vector< vk::DeviceQueueCreateInfo > getQueueCreateInfos( PhysicalDevice& );
vk::DeviceCreateInfo getCreateInfo( PhysicalDevice& );
DeviceCreateInfo( PhysicalDevice& );
} device_creation_info;
vk::raii::Device m_device;
vk::raii::CommandPool m_commandPool;
vk::raii::Queue m_graphics_queue;
vk::raii::Queue m_present_queue;
VmaAllocator m_allocator;
void copyBuffer(
vk::Buffer dst,
@@ -60,7 +80,10 @@ namespace fgl::engine
vk::PhysicalDeviceProperties m_properties {};
Device( Window& window, Instance& );
vk::CommandPoolCreateInfo commandPoolInfo();
Device( Window&, Instance& );
~Device();
static Device& getInstance();
@@ -85,20 +108,12 @@ namespace fgl::engine
private:
void setupDebugMessenger();
void createSurface( Window& window );
void pickPhysicalDevice();
void createLogicalDevice();
void createVMAAllocator();
void createCommandPool();
VmaAllocator createVMAAllocator();
// helper functions
bool isDeviceSuitable( vk::PhysicalDevice device );
std::vector< const char* > getRequiredInstanceExtensions();
bool checkValidationLayerSupport();
QueueFamilyIndices findQueueFamilies( vk::PhysicalDevice device );
bool checkDeviceExtensionSupport( vk::PhysicalDevice device );
SwapChainSupportDetails querySwapChainSupport( vk::PhysicalDevice device );
bool checkDeviceExtensionSupport( vk::raii::PhysicalDevice device );
SwapChainSupportDetails querySwapChainSupport( vk::raii::PhysicalDevice device );
public:
@@ -112,28 +127,24 @@ namespace fgl::engine
public:
Device( Window& window );
~Device();
// Not copyable or movable
Device( const Device& ) = delete;
Device& operator=( const Device& ) = delete;
Device( Device&& ) = delete;
Device& operator=( Device&& ) = delete;
FGL_DELETE_DEFAULT_CTOR( Device )
FGL_DELETE_COPY( Device )
FGL_DELETE_MOVE( Device )
vk::CommandPool getCommandPool() { return m_commandPool; }
vk::Device device() { return m_device; }
vk::raii::Device& device() { return m_device; }
vk::Instance instance() { return m_instance; }
Instance& instance() { return m_instance; }
vk::PhysicalDevice phyDevice() { return m_physical_device; }
PhysicalDevice& phyDevice() { return m_physical_device; }
vk::SurfaceKHR surface() { return m_surface_khr; }
vk::Queue graphicsQueue() { return m_graphics_queue; }
vk::raii::Queue& graphicsQueue() { return m_graphics_queue; }
vk::Queue presentQueue() { return m_present_queue; }
vk::raii::Queue& presentQueue() { return m_present_queue; }
VmaAllocator allocator() { return m_allocator; }
@@ -141,16 +152,18 @@ namespace fgl::engine
uint32_t findMemoryType( uint32_t typeFilter, vk::MemoryPropertyFlags properties );
QueueFamilyIndices findPhysicalQueueFamilies() { return findQueueFamilies( m_physical_device ); }
vk::Format findSupportedFormat(
const std::vector< vk::Format >& candidates, vk::ImageTiling tiling, vk::FormatFeatureFlags features );
vk::CommandBuffer beginSingleTimeCommands();
void endSingleTimeCommands( vk::CommandBuffer commandBuffer );
vk::raii::CommandBuffer beginSingleTimeCommands();
void endSingleTimeCommands( vk::raii::CommandBuffer& commandBuffer );
void copyBufferToImage(
vk::Buffer buffer, vk::Image image, uint32_t width, uint32_t height, uint32_t layerCount );
VkDevice operator*() { return *m_device; }
vk::raii::Device* operator->() { return &m_device; }
};
} // namespace fgl::engine

View File

@@ -44,6 +44,8 @@ namespace fgl::engine
~Instance();
inline vk::raii::Instance& handle() { return m_instance; }
inline operator vk::Instance() { return m_instance; }
inline operator VkInstance() { return *m_instance; }

View File

@@ -0,0 +1,104 @@
//
// Created by kj16609 on 6/20/24.
//
#include "PhysicalDevice.hpp"
#include "Instance.hpp"
#include "Surface.hpp"
#include "engine/logging/logging.hpp"
namespace fgl::engine
{
PhysicalDevice::PhysicalDevice( Instance& instance, Surface& surface ) :
m_phy_device( pickPhysicalDevice( instance, surface ) ),
queue_pool( m_phy_device, surface )
{}
//! Checks that a device has a graphics capable queue and can present to the given surface.
bool isDeviceSupported( const vk::raii::PhysicalDevice& device, Surface& surface )
{
const std::vector< vk::QueueFamilyProperties > family_props { device.getQueueFamilyProperties() };
bool has_graphics_queue { false };
bool has_present_queue { false };
int idx { 0 };
for ( const auto& queue_family : family_props )
{
if ( queue_family.queueCount <= 0 )
{
++idx;
continue;
}
// We need at least 1 queue that can do graphics.
if ( queue_family.queueFlags & vk::QueueFlagBits::eGraphics )
{
log::debug( "Graphics capable queue found at idx: {}", idx );
has_graphics_queue = true;
}
// Check if the device can present to the surface.
vk::Bool32 can_present { device.getSurfaceSupportKHR( idx, surface ) };
if ( can_present == VK_TRUE )
{
log::debug( "Present capable queue found at idx: {}", idx );
has_present_queue = true;
}
if ( has_graphics_queue && has_present_queue ) return true;
++idx;
}
return false;
}
vk::raii::PhysicalDevice PhysicalDevice::pickPhysicalDevice( Instance& instance, Surface& surface )
{
std::vector< vk::raii::PhysicalDevice > devices { instance.handle().enumeratePhysicalDevices() };
for ( auto& device : devices )
{
if ( isDeviceSupported( device, surface ) )
{
//We found a device we can use.
log::info( "Found device for surface" );
vk::raii::PhysicalDevice selected_device { std::move( device ) };
return selected_device;
}
}
throw std::runtime_error( "Failed to find a valid device" );
}
const static std::vector< const char* > required_device_extensions { VK_KHR_SWAPCHAIN_EXTENSION_NAME,
VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME };
bool PhysicalDevice::supportsRequiredExtensions()
{
const std::vector< vk::ExtensionProperties > device_extentions {
m_phy_device.enumerateDeviceExtensionProperties()
};
for ( const auto required : required_device_extensions )
{
if ( std::find_if(
device_extentions.begin(),
device_extentions.end(),
[ &required ]( const vk::ExtensionProperties& props )
{ return std::strcmp( props.extensionName, required ); } )
== device_extentions.end() )
{
return false;
}
}
return true;
}
} // namespace fgl::engine

View File

@@ -0,0 +1,43 @@
//
// Created by kj16609 on 6/20/24.
//
#pragma once
#include <vulkan/vulkan_raii.hpp>
#include "QueuePool.hpp"
#include "engine/FGL_DEFINES.hpp"
namespace fgl::engine
{
class Instance;
class Surface;
class PhysicalDevice
{
vk::raii::PhysicalDevice m_phy_device;
QueuePool queue_pool;
FGL_DELETE_ALL_Ro5( PhysicalDevice );
//! Picks a device that can render to the desired output window
vk::raii::PhysicalDevice pickPhysicalDevice( Instance& dev, Surface& surface );
bool supportsRequiredExtensions();
public:
QueuePool& queueInfo() { return queue_pool; }
PhysicalDevice( Instance& instance, Surface& surface );
vk::raii::PhysicalDevice& handle() { return m_phy_device; }
operator vk::raii::PhysicalDevice() { return m_phy_device; }
VkPhysicalDevice operator*() { return *m_phy_device; }
vk::raii::PhysicalDevice* operator->() { return &m_phy_device; }
};
} // namespace fgl::engine

View File

@@ -0,0 +1,49 @@
//
// Created by kj16609 on 6/20/24.
//
#include "QueuePool.hpp"
#include "Attachment.hpp"
#include "PhysicalDevice.hpp"
namespace fgl::engine
{
QueuePool::QueuePool( vk::raii::PhysicalDevice& physical_device, Surface& surface )
{
const auto family_props { physical_device.getQueueFamilyProperties() };
for ( std::uint32_t i = 0; i < family_props.size(); ++i )
{
auto& props { family_props[ i ] };
if ( props.queueCount > 0 )
{
vk::Bool32 can_present { physical_device.getSurfaceSupportKHR( i, surface ) };
queue_info.emplace_back( props, can_present == VK_TRUE, 0 );
}
}
}
QueuePool::QueueIndex QueuePool::getIndex( const vk::QueueFlags flags )
{
for ( std::uint32_t i = 0; i < queue_info.size(); ++i )
{
const auto& [ props, can_present, num_allocated ] = queue_info[ i ];
if ( props.queueFlags & flags && props.queueCount > 0 ) return i;
}
throw std::runtime_error( "Failed to get index of queue family with given flags" );
}
std::uint32_t QueuePool::getPresentIndex()
{
for ( std::uint32_t i = 0; i < queue_info.size(); ++i )
{
if ( queue_info[ i ].can_present ) return i;
}
}
} // namespace fgl::engine

View File

@@ -0,0 +1,48 @@
//
// Created by kj16609 on 6/20/24.
//
#pragma once
#include <vulkan/vulkan.hpp>
#include <vulkan/vulkan_raii.hpp>
#include <set>
#include "engine/FGL_DEFINES.hpp"
namespace fgl::engine
{
class Surface;
class PhysicalDevice;
class Queue;
class QueuePool
{
struct QueueInfo
{
vk::QueueFamilyProperties props;
bool can_present;
std::uint8_t allocated { 0 };
};
std::vector< QueueInfo > queue_info {};
public:
QueuePool( vk::raii::PhysicalDevice&, Surface& );
FGL_DELETE_ALL_Ro5( QueuePool );
Queue allocate();
Queue allocateIndex( const std::uint32_t idx );
using QueueIndex = std::uint32_t;
//! Returns a unique list of indexes with the matching flags
QueueIndex getIndex( const vk::QueueFlags flags );
std::uint32_t getPresentIndex();
};
} // namespace fgl::engine

View File

@@ -9,9 +9,9 @@
namespace fgl::engine
{
vk::RenderPass RenderPass::create()
vk::raii::RenderPass RenderPass::create()
{
auto device { Device::getInstance().device() };
auto& device { Device::getInstance() };
vk::RenderPassCreateInfo info;
@@ -24,12 +24,7 @@ namespace fgl::engine
vk::RenderPass render_pass {};
if ( device.createRenderPass( &info, nullptr, &render_pass ) != vk::Result::eSuccess ) [[unlikely]]
{
throw std::runtime_error( "failed to create render pass!" );
}
else
return render_pass;
return device->createRenderPass( info );
}
} // namespace fgl::engine

View File

@@ -101,7 +101,7 @@ namespace fgl::engine
return std::make_unique< RenderPassResources >( std::move( views ) );
}
vk::RenderPass create();
vk::raii::RenderPass create();
};
} // namespace fgl::engine

View File

@@ -25,36 +25,35 @@
namespace fgl::engine
{
Renderer::Renderer( Window& window ) : m_window( window )
Renderer::Renderer( Window& window, PhysicalDevice& phy_device ) :
m_window( window ),
m_phy_device( phy_device ),
m_swapchain( std::make_unique< SwapChain >( m_window.getExtent(), m_phy_device ) )
{
recreateSwapchain();
createCommandBuffers();
}
Renderer::~Renderer()
{}
TracyVkCtx createContext( PhysicalDevice& physical_device, Device& device, vk::raii::CommandBuffer& cmd_buffer )
{
freeCommandBuffers();
return TracyVkContext( *physical_device, *device, *device.graphicsQueue(), *cmd_buffer );
}
void Renderer::createCommandBuffers()
{
m_command_buffer.resize( SwapChain::MAX_FRAMES_IN_FLIGHT );
vk::CommandBufferAllocateInfo alloc_info {};
alloc_info.pNext = VK_NULL_HANDLE;
alloc_info.commandPool = Device::getInstance().getCommandPool();
alloc_info.level = vk::CommandBufferLevel::ePrimary;
alloc_info.commandBufferCount = static_cast< std::uint32_t >( m_command_buffer.size() );
alloc_info.commandBufferCount = SwapChain::MAX_FRAMES_IN_FLIGHT;
if ( Device::getInstance().device().allocateCommandBuffers( &alloc_info, m_command_buffer.data() )
!= vk::Result::eSuccess )
throw std::runtime_error( "Failed to allocate command buffers" );
m_command_buffer = Device::getInstance().device().allocateCommandBuffers( alloc_info );
#if TRACY_ENABLE
VkPhysicalDevice phy_dev { Device::getInstance().phyDevice() };
VkDevice dev { Device::getInstance().device() };
m_tracy_ctx = TracyVkContext( phy_dev, dev, Device::getInstance().graphicsQueue(), m_command_buffer[ 0 ] );
m_tracy_ctx = createContext( m_phy_device, Device::getInstance(), m_command_buffer[ 0 ] );
/*
m_tracy_ctx = TracyVkContextCalibrated(
@@ -83,7 +82,7 @@ namespace fgl::engine
Device::getInstance().device().waitIdle();
if ( m_swapchain == nullptr )
m_swapchain = std::make_unique< SwapChain >( extent );
m_swapchain = std::make_unique< SwapChain >( extent, m_phy_device );
else
{
std::shared_ptr< SwapChain > old_swap_chain { std::move( m_swapchain ) };
@@ -94,17 +93,7 @@ namespace fgl::engine
}
}
void Renderer::freeCommandBuffers()
{
if ( m_command_buffer.size() == 0 ) return;
Device::getInstance().device().freeCommandBuffers(
Device::getInstance().getCommandPool(),
static_cast< std::uint32_t >( m_command_buffer.size() ),
m_command_buffer.data() );
}
vk::CommandBuffer Renderer::beginFrame()
vk::raii::CommandBuffer& Renderer::beginFrame()
{
assert( !is_frame_started && "Cannot begin frame while frame is already in progress" );
auto [ result, image_idx ] = m_swapchain->acquireNextImage();
@@ -113,7 +102,6 @@ namespace fgl::engine
if ( result == vk::Result::eErrorOutOfDateKHR )
{
recreateSwapchain();
return nullptr;
}
if ( result != vk::Result::eSuccess && result != vk::Result::eSuboptimalKHR )
@@ -137,12 +125,11 @@ namespace fgl::engine
ZoneScopedN( "Ending frame" );
assert( is_frame_started && "Cannot call end frame while frame is not in progress" );
auto command_buffer { getCurrentCommandbuffer() };
auto& command_buffer { getCurrentCommandbuffer() };
if ( vkEndCommandBuffer( command_buffer ) != VK_SUCCESS )
throw std::runtime_error( "Failed to end recording command buffer" );
command_buffer.end();
const auto result { m_swapchain->submitCommandBuffers( &command_buffer, current_image_idx ) };
const auto result { m_swapchain->submitCommandBuffers( command_buffer, current_image_idx ) };
if ( result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR
|| m_window.wasWindowResized() )
@@ -157,11 +144,11 @@ namespace fgl::engine
current_frame_idx = static_cast< std::uint16_t >( ( current_frame_idx + 1 ) % SwapChain::MAX_FRAMES_IN_FLIGHT );
}
void Renderer::beginSwapchainRendererPass( vk::CommandBuffer buffer )
void Renderer::beginSwapchainRendererPass( vk::raii::CommandBuffer& buffer )
{
assert( is_frame_started && "Cannot call beginSwapChainRenderPass if frame is not in progress" );
assert(
buffer == getCurrentCommandbuffer()
*buffer == *getCurrentCommandbuffer()
&& "Cannot begin render pass on command buffer from a different frame" );
std::vector< vk::ClearValue > clear_values { m_swapchain->getClearValues() };
@@ -174,7 +161,7 @@ namespace fgl::engine
render_pass_info.clearValueCount = static_cast< std::uint32_t >( clear_values.size() );
render_pass_info.pClearValues = clear_values.data();
buffer.beginRenderPass( &render_pass_info, vk::SubpassContents::eInline );
buffer.beginRenderPass( render_pass_info, vk::SubpassContents::eInline );
vk::Viewport viewport {};
viewport.x = 0.0f;
@@ -185,16 +172,21 @@ namespace fgl::engine
viewport.maxDepth = 1.0f;
vk::Rect2D scissor { { 0, 0 }, m_swapchain->getSwapChainExtent() };
buffer.setViewport( 0, 1, &viewport );
buffer.setScissor( 0, 1, &scissor );
std::vector< vk::Viewport > viewports { viewport };
std::vector< vk::Rect2D > scissors { scissor };
buffer.setViewport( 0, viewports );
buffer.setScissor( 0, scissors );
}
void Renderer::endSwapchainRendererPass( vk::CommandBuffer buffer )
void Renderer::endSwapchainRendererPass( vk::raii::CommandBuffer& buffer )
{
assert( is_frame_started && "Cannot call endSwapChainRenderPass if frame is not in progress" );
assert(
buffer == getCurrentCommandbuffer() && "Cannot end render pass on command buffer from a different frame" );
*buffer == *getCurrentCommandbuffer()
&& "Cannot end render pass on command buffer from a different frame" );
vkCmdEndRenderPass( buffer );
buffer.endRenderPass();
}
} // namespace fgl::engine

View File

@@ -25,14 +25,14 @@ namespace fgl::engine
class Renderer
{
Window& m_window;
std::unique_ptr< SwapChain > m_swapchain { std::make_unique< SwapChain >( m_window.getExtent() ) };
PhysicalDevice& m_phy_device;
std::unique_ptr< SwapChain > m_swapchain;
std::vector< vk::CommandBuffer > m_command_buffer {};
std::vector< vk::raii::CommandBuffer> m_command_buffer {};
std::optional< TracyVkCtx > m_tracy_ctx { std::nullopt };
void createCommandBuffers();
void freeCommandBuffers();
void recreateSwapchain();
uint32_t current_image_idx { std::numeric_limits< std::uint32_t >::max() };
@@ -54,7 +54,7 @@ namespace fgl::engine
bool isFrameInProgress() const { return is_frame_started; }
vk::CommandBuffer& getCurrentCommandbuffer()
vk::raii::CommandBuffer& getCurrentCommandbuffer()
{
assert( is_frame_started && "Cannot get command buffer while frame not in progress" );
return m_command_buffer[ current_frame_idx ];
@@ -70,16 +70,16 @@ namespace fgl::engine
#endif
}
vk::RenderPass getSwapChainRenderPass() const { return m_swapchain->getRenderPass(); }
vk::raii::RenderPass& getSwapChainRenderPass() const { return m_swapchain->getRenderPass(); }
float getAspectRatio() const { return m_swapchain->extentAspectRatio(); }
vk::CommandBuffer beginFrame();
vk::raii::CommandBuffer& beginFrame();
void endFrame();
void beginSwapchainRendererPass( vk::CommandBuffer buffer );
void endSwapchainRendererPass( vk::CommandBuffer buffer );
void beginSwapchainRendererPass( vk::raii::CommandBuffer& buffer );
void endSwapchainRendererPass( vk::raii::CommandBuffer& buffer );
Renderer( Window& window );
Renderer( Window& window, PhysicalDevice& phy_device );
~Renderer();
Renderer( Renderer&& other ) = delete;
Renderer( const Renderer& other ) = delete;

View File

@@ -0,0 +1,15 @@
//
// Created by kj16609 on 6/20/24.
//
#include "Surface.hpp"
#include "engine/Window.hpp"
namespace fgl::engine
{
Surface::Surface( Window& window, Instance& instance ) : m_surface( window.createWindowSurface( instance ) )
{}
} // namespace fgl::engine

View File

@@ -0,0 +1,31 @@
//
// Created by kj16609 on 6/20/24.
//
#pragma once
#include <vulkan/vulkan_raii.hpp>
#include "engine/FGL_DEFINES.hpp"
namespace fgl::engine
{
class Window;
class Instance;
class Surface
{
vk::raii::SurfaceKHR m_surface;
public:
Surface( Window& window, Instance& instance );
FGL_DELETE_ALL_Ro5( Surface );
vk::raii::SurfaceKHR& handle() { return m_surface; }
operator vk::SurfaceKHR() { return m_surface; }
operator VkSurfaceKHR() { return *m_surface; }
};
} // namespace fgl::engine

View File

@@ -16,12 +16,15 @@
namespace fgl::engine
{
SwapChain::SwapChain( vk::Extent2D extent ) : windowExtent( extent )
SwapChain::SwapChain( vk::Extent2D extent, PhysicalDevice& phy_device ) :
m_phy_device( phy_device ),
windowExtent( extent )
{
init();
}
SwapChain::SwapChain( vk::Extent2D extent, std::shared_ptr< SwapChain > previous ) :
m_phy_device( previous->m_phy_device ),
windowExtent( extent ),
old_swap_chain( previous )
{
@@ -37,65 +40,38 @@ namespace fgl::engine
createSyncObjects();
}
SwapChain::~SwapChain()
{
ZoneScoped;
if ( swapChain != nullptr )
{
vkDestroySwapchainKHR( Device::getInstance().device(), swapChain, nullptr );
swapChain = nullptr;
}
for ( auto framebuffer : m_swap_chain_buffers )
{
vkDestroyFramebuffer( Device::getInstance().device(), framebuffer, nullptr );
}
vkDestroyRenderPass( Device::getInstance().device(), m_render_pass, nullptr );
// cleanup synchronization objects
for ( size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++ )
{
vkDestroySemaphore( Device::getInstance().device(), renderFinishedSemaphores[ i ], nullptr );
vkDestroySemaphore( Device::getInstance().device(), imageAvailableSemaphores[ i ], nullptr );
vkDestroyFence( Device::getInstance().device(), inFlightFences[ i ], nullptr );
}
}
std::pair< vk::Result, std::uint32_t > SwapChain::acquireNextImage()
{
ZoneScoped;
if ( Device::getInstance()
.device()
.waitForFences( 1, &inFlightFences[ currentFrame ], VK_TRUE, std::numeric_limits< uint64_t >::max() )
std::vector< vk::Fence > fences { inFlightFences[ currentFrame ] };
if ( Device::getInstance().device().waitForFences( fences, VK_TRUE, std::numeric_limits< uint64_t >::max() )
!= vk::Result::eSuccess )
throw std::runtime_error( "failed to wait for fences!" );
std::uint32_t image_idx { 0 };
vk::Result result { Device::getInstance().device().acquireNextImageKHR(
swapChain,
auto result { swapChain.acquireNextImage(
std::numeric_limits< uint64_t >::max(),
imageAvailableSemaphores[ currentFrame ], // must be a not signaled semaphore
VK_NULL_HANDLE,
&image_idx ) };
imageAvailableSemaphores[ currentFrame ] // must be a not signaled semaphore
) };
return { result, image_idx };
return result;
}
vk::Result SwapChain::submitCommandBuffers( const vk::CommandBuffer* buffers, std::uint32_t imageIndex )
vk::Result SwapChain::submitCommandBuffers( const vk::raii::CommandBuffer& buffers, std::uint32_t imageIndex )
{
ZoneScoped;
if ( imagesInFlight[ imageIndex ] != VK_NULL_HANDLE )
{
if ( Device::getInstance()
.device()
.waitForFences( 1, &imagesInFlight[ imageIndex ], VK_TRUE, std::numeric_limits< uint64_t >::max() )
!= vk::Result::eSuccess )
throw std::runtime_error( "failed to wait for fences!" );
}
imagesInFlight[ imageIndex ] = inFlightFences[ currentFrame ];
std::vector< vk::Fence > fences { imagesInFlight[ imageIndex ] };
if ( Device::getInstance().device().waitForFences( fences, VK_TRUE, std::numeric_limits< uint64_t >::max() )
!= vk::Result::eSuccess )
throw std::runtime_error( "failed to wait for fences!" );
vk::SubmitInfo submitInfo {};
vk::Semaphore waitSemaphores[] = { imageAvailableSemaphores[ currentFrame ] };
@@ -105,36 +81,17 @@ namespace fgl::engine
submitInfo.pWaitDstStageMask = waitStages;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = buffers;
submitInfo.pCommandBuffers = &( *buffers );
vk::Semaphore signalSemaphores[] = { renderFinishedSemaphores[ currentFrame ] };
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = signalSemaphores;
if ( Device::getInstance().device().resetFences( 1, &inFlightFences[ currentFrame ] ) != vk::Result::eSuccess )
throw std::runtime_error( "failed to reset fences!" );
Device::getInstance().device().resetFences( fences );
if ( auto result =
Device::getInstance().graphicsQueue().submit( 1, &submitInfo, inFlightFences[ currentFrame ] );
result != vk::Result::eSuccess )
{
#pragma GCC diagnostic push
//Can't possibly handle all of these. It will add like....100 lines of just empty cases
#pragma GCC diagnostic ignored "-Wswitch-enum"
switch ( result )
{
case vk::Result::eErrorOutOfDateKHR:
return vk::Result::eErrorOutOfDateKHR;
case vk::Result::eSuboptimalKHR:
return vk::Result::eSuboptimalKHR;
case vk::Result::eErrorDeviceLost:
throw std::runtime_error( "Device lost!" );
default:
throw std::runtime_error(
"failed to submit draw command buffer!: ID" + std::to_string( static_cast< int >( result ) ) );
}
#pragma GCC diagnostic pop
}
std::vector< vk::SubmitInfo > submit_infos { submitInfo };
Device::getInstance().graphicsQueue().submit( submitInfo, inFlightFences[ currentFrame ] );
vk::PresentInfoKHR presentInfo = {};
@@ -148,7 +105,7 @@ namespace fgl::engine
std::array< std::uint32_t, 1 > indicies { { imageIndex } };
presentInfo.setImageIndices( indicies );
if ( auto present_result = Device::getInstance().presentQueue().presentKHR( &presentInfo );
if ( auto present_result = Device::getInstance().presentQueue().presentKHR( presentInfo );
present_result != vk::Result::eSuccess )
{
if ( present_result == vk::Result::eSuboptimalKHR ) return vk::Result::eSuboptimalKHR;
@@ -187,11 +144,15 @@ namespace fgl::engine
createInfo.imageArrayLayers = 1;
createInfo.imageUsage = vk::ImageUsageFlagBits::eColorAttachment;
QueueFamilyIndices indices = Device::getInstance().findPhysicalQueueFamilies();
uint32_t queueFamilyIndices[] = { indices.graphicsFamily, indices.presentFamily };
std::uint32_t graphics_family { m_phy_device.queueInfo().getIndex( vk::QueueFlagBits::eGraphics ) };
std::uint32_t present_family { m_phy_device.queueInfo().getPresentIndex() };
if ( indices.graphicsFamily != indices.presentFamily )
const uint32_t queueFamilyIndices[] = { graphics_family, present_family };
if ( graphics_family != present_family )
{
// If the familys are not the same then the swapchain must be shared between
// both queues.
createInfo.imageSharingMode = vk::SharingMode::eConcurrent;
createInfo.queueFamilyIndexCount = 2;
createInfo.pQueueFamilyIndices = queueFamilyIndices;
@@ -209,17 +170,11 @@ namespace fgl::engine
createInfo.presentMode = presentMode;
createInfo.clipped = VK_TRUE;
createInfo.oldSwapchain = old_swap_chain == nullptr ? VK_NULL_HANDLE : old_swap_chain->swapChain;
createInfo.oldSwapchain = old_swap_chain == nullptr ? VK_NULL_HANDLE : *old_swap_chain->swapChain;
if ( Device::getInstance().device().createSwapchainKHR( &createInfo, nullptr, &swapChain )
!= vk::Result::eSuccess )
{
throw std::runtime_error( "failed to create swap chain!" );
}
swapChain = Device::getInstance()->createSwapchainKHR( createInfo );
std::vector< vk::Image > swap_chain_images {
Device::getInstance().device().getSwapchainImagesKHR( swapChain )
};
std::vector< vk::Image > swap_chain_images { swapChain.getImages() };
for ( std::uint64_t i = 0; i < swap_chain_images.size(); i++ )
{
@@ -352,7 +307,8 @@ namespace fgl::engine
void SwapChain::createFramebuffers()
{
ZoneScoped;
m_swap_chain_buffers.resize( imageCount() );
m_swap_chain_buffers.clear();
m_swap_chain_buffers.reserve( imageCount() );
for ( uint8_t i = 0; i < imageCount(); i++ )
{
std::vector< vk::ImageView > attachments { m_render_pass_resources->forFrame( i ) };
@@ -367,22 +323,16 @@ namespace fgl::engine
framebufferInfo.height = swapChainExtent.height;
framebufferInfo.layers = 1;
if ( Device::getInstance()
.device()
.createFramebuffer( &framebufferInfo, nullptr, &( m_swap_chain_buffers[ i ] ) )
!= vk::Result::eSuccess )
{
throw std::runtime_error( "failed to create framebuffer!" );
}
m_swap_chain_buffers.push_back( Device::getInstance()->createFramebuffer( framebufferInfo ) );
}
}
void SwapChain::createSyncObjects()
{
ZoneScoped;
imageAvailableSemaphores.resize( MAX_FRAMES_IN_FLIGHT );
renderFinishedSemaphores.resize( MAX_FRAMES_IN_FLIGHT );
inFlightFences.resize( MAX_FRAMES_IN_FLIGHT );
imageAvailableSemaphores.reserve( MAX_FRAMES_IN_FLIGHT );
renderFinishedSemaphores.reserve( MAX_FRAMES_IN_FLIGHT );
inFlightFences.reserve( MAX_FRAMES_IN_FLIGHT );
imagesInFlight.resize( imageCount(), VK_NULL_HANDLE );
vk::SemaphoreCreateInfo semaphoreInfo {};
@@ -392,15 +342,11 @@ namespace fgl::engine
for ( size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++ )
{
auto vk_device { Device::getInstance().device() };
if ( vk_device.createSemaphore( &semaphoreInfo, nullptr, &imageAvailableSemaphores[ i ] )
!= vk::Result::eSuccess )
throw std::runtime_error( "failed to create image available semaphore!" );
if ( vk_device.createSemaphore( &semaphoreInfo, nullptr, &renderFinishedSemaphores[ i ] )
!= vk::Result::eSuccess )
throw std::runtime_error( "failed to create render finished semaphore!" );
if ( vk_device.createFence( &fenceInfo, nullptr, &inFlightFences[ i ] ) != vk::Result::eSuccess )
throw std::runtime_error( "failed to create in flight fence!" );
auto& device { Device::getInstance() };
imageAvailableSemaphores.push_back( device->createSemaphore( semaphoreInfo ) );
renderFinishedSemaphores.push_back( device->createSemaphore( semaphoreInfo ) );
inFlightFences.push_back( device->createFence( fenceInfo ) );
}
}

View File

@@ -24,24 +24,26 @@ namespace fgl::engine
private:
PhysicalDevice& m_phy_device;
vk::Format m_swap_chain_format { vk::Format::eUndefined };
vk::Format m_swap_chain_depth_format { findDepthFormat() };
vk::Extent2D m_swap_chain_extent { 0, 0 };
std::vector< vk::Framebuffer > m_swap_chain_buffers {};
vk::RenderPass m_render_pass { VK_NULL_HANDLE };
std::vector< vk::raii::Framebuffer > m_swap_chain_buffers {};
vk::raii::RenderPass m_render_pass { VK_NULL_HANDLE };
std::unique_ptr< RenderPassResources > m_render_pass_resources { nullptr };
std::vector< Image > m_swap_chain_images {};
vk::Extent2D windowExtent;
vk::SwapchainKHR swapChain { VK_NULL_HANDLE };
vk::raii::SwapchainKHR swapChain { VK_NULL_HANDLE };
std::shared_ptr< SwapChain > old_swap_chain {};
std::vector< vk::Semaphore > imageAvailableSemaphores {};
std::vector< vk::Semaphore > renderFinishedSemaphores {};
std::vector< vk::Fence > inFlightFences {};
std::vector< vk::raii::Semaphore > imageAvailableSemaphores {};
std::vector< vk::raii::Semaphore > renderFinishedSemaphores {};
std::vector< vk::raii::Fence > inFlightFences {};
std::vector< vk::Fence > imagesInFlight {};
size_t currentFrame { 0 };
@@ -73,19 +75,18 @@ namespace fgl::engine
return *m_gbuffer_descriptor_set[ frame_idx ];
}
SwapChain( vk::Extent2D windowExtent );
SwapChain( vk::Extent2D windowExtent, PhysicalDevice& phy_dev );
SwapChain( vk::Extent2D windowExtent, std::shared_ptr< SwapChain > previous );
~SwapChain();
SwapChain( const SwapChain& ) = delete;
SwapChain& operator=( const SwapChain& ) = delete;
vk::Framebuffer getFrameBuffer( std::uint32_t index ) const
vk::raii::Framebuffer& getFrameBuffer( std::uint32_t index )
{
return m_swap_chain_buffers[ static_cast< std::size_t >( index ) ];
}
vk::RenderPass getRenderPass() const { return m_render_pass; }
vk::raii::RenderPass& getRenderPass() { return m_render_pass; }
std::uint16_t imageCount() const { return static_cast< std::uint16_t >( m_swap_chain_images.size() ); }
@@ -112,7 +113,8 @@ namespace fgl::engine
vk::Format findDepthFormat();
[[nodiscard]] std::pair< vk::Result, std::uint32_t > acquireNextImage();
[[nodiscard]] vk::Result submitCommandBuffers( const vk::CommandBuffer* buffers, std::uint32_t imageIndex );
[[nodiscard]] vk::Result
submitCommandBuffers( const vk::raii::CommandBuffer& buffers, std::uint32_t imageIndex );
};
} // namespace fgl::engine

View File

@@ -9,7 +9,7 @@
namespace fgl::engine
{
CompositionSystem::CompositionSystem( [[maybe_unused]] Device& device, VkRenderPass render_pass )
CompositionSystem::CompositionSystem( [[maybe_unused]] Device& device, vk::raii::RenderPass& render_pass )
{
PipelineConfigInfo composition_info { render_pass };
PipelineConfigInfo::addColorAttachmentConfig( composition_info );
@@ -17,11 +17,11 @@ namespace fgl::engine
PipelineConfigInfo::disableCulling( composition_info );
composition_info.subpass = 1;
m_pipeline = std::make_unique< CompositionPipeline >( Device::getInstance(), composition_info );
m_pipeline = std::make_unique< CompositionPipeline >( Device::getInstance(), std::move( composition_info ) );
m_pipeline->setDebugName( "Composition pipeline" );
}
vk::CommandBuffer& CompositionSystem::setupSystem( FrameInfo& info )
vk::raii::CommandBuffer& CompositionSystem::setupSystem( FrameInfo& info )
{
auto& command_buffer { info.command_buffer };
m_pipeline->bind( command_buffer );

View File

@@ -25,11 +25,11 @@ namespace fgl::engine
std::unique_ptr< CompositionPipeline > m_pipeline { nullptr };
vk::CommandBuffer& setupSystem( FrameInfo& info );
vk::raii::CommandBuffer& setupSystem( FrameInfo& info );
public:
CompositionSystem( Device& device, VkRenderPass render_pass );
CompositionSystem( Device& device, vk::raii::RenderPass& render_pass );
~CompositionSystem() = default;
void pass( FrameInfo& info );

View File

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

View File

@@ -12,6 +12,7 @@
#include "DrawPair.hpp"
#include "engine/literals/size.hpp"
#include "engine/tree/octtree/OctTreeNode.hpp"
#include "spdlog/fmt/bundled/compile.h"
namespace fgl::engine
{
@@ -25,22 +26,19 @@ namespace fgl::engine
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eDeviceLocal );
}
EntityRendererSystem::EntityRendererSystem( Device& device, VkRenderPass render_pass ) : m_device( device )
EntityRendererSystem::EntityRendererSystem( Device& device, vk::raii::RenderPass& render_pass ) : m_device( device )
{
ZoneScoped;
PipelineConfigInfo info { render_pass };
for ( int i = 0; i < 4; ++i ) PipelineConfigInfo::addColorAttachmentConfig( info );
info.subpass = 0;
//m_pipeline = std::make_unique< Pipeline >( m_device, info );
//m_pipeline->setDebugName( "Entity pipeline" );
m_standard_pipeline = std::make_unique< StandardPipeline >( m_device, info );
m_textured_pipeline = std::make_unique< TexturedPipeline >( m_device, info );
PipelineConfigInfo standard_info { render_pass };
for ( int i = 0; i < 4; ++i ) PipelineConfigInfo::addColorAttachmentConfig( standard_info );
standard_info.subpass = 0;
m_standard_pipeline = std::make_unique< StandardPipeline >( m_device, std::move( standard_info ) );
m_standard_pipeline->setDebugName( "Standard entity pipeline" );
PipelineConfigInfo textured_info { render_pass };
for ( int i = 0; i < 4; ++i ) PipelineConfigInfo::addColorAttachmentConfig( textured_info );
textured_info.subpass = 0;
m_textured_pipeline = std::make_unique< TexturedPipeline >( m_device, std::move( textured_info ) );
m_textured_pipeline->setDebugName( "Textured entity pipeline" );
using namespace fgl::literals::size_literals;
@@ -50,7 +48,7 @@ namespace fgl::engine
initDrawParameterBuffer( 1_KiB );
}
vk::CommandBuffer& EntityRendererSystem::setupSystem( FrameInfo& info )
vk::raii::CommandBuffer& EntityRendererSystem::setupSystem( FrameInfo& info )
{
auto& command_buffer { info.command_buffer };
@@ -69,7 +67,7 @@ namespace fgl::engine
{
ZoneScopedN( "Entity pass" );
auto& command_buffer { setupSystem( info ) };
TracyVkZone( info.tracy_ctx, command_buffer, "Render entities" );
TracyVkZone( info.tracy_ctx, *command_buffer, "Render entities" );
texturelessPass( info );
//texturedPass( info );
@@ -120,7 +118,7 @@ namespace fgl::engine
{
ZoneScopedN( "Textureless pass" );
auto& command_buffer { info.command_buffer };
TracyVkZone( info.tracy_ctx, command_buffer, "Render textureless entities" );
TracyVkZone( info.tracy_ctx, *command_buffer, "Render textureless entities" );
//Bind pipeline
m_standard_pipeline->bind( command_buffer );

View File

@@ -75,7 +75,7 @@ namespace fgl::engine
vk::MemoryPropertyFlagBits::eDeviceLocal );
}
vk::CommandBuffer& setupSystem( FrameInfo& );
vk::raii::CommandBuffer& setupSystem( FrameInfo& );
public:
@@ -87,7 +87,7 @@ namespace fgl::engine
void texturelessPass( FrameInfo& info );
void texturedPass( FrameInfo& info );
EntityRendererSystem( Device& device, VkRenderPass render_pass );
EntityRendererSystem( Device& device, vk::raii::RenderPass& render_pass );
~EntityRendererSystem() = default;
EntityRendererSystem( EntityRendererSystem&& other ) = delete;
EntityRendererSystem( const EntityRendererSystem& other ) = delete;

View File

@@ -11,7 +11,7 @@
namespace fgl::engine
{
TerrainSystem::TerrainSystem( Device& device, VkRenderPass render_pass )
TerrainSystem::TerrainSystem( Device& device, vk::raii::RenderPass& render_pass )
{
ZoneScoped;
PipelineConfigInfo info { render_pass };
@@ -25,7 +25,7 @@ namespace fgl::engine
info.subpass = 0;
m_pipeline = std::make_unique< Pipeline >( device, info );
m_pipeline = std::make_unique< Pipeline >( device, std::move( info ) );
m_pipeline->setDebugName( "Terrain pipeline" );
using namespace fgl::literals::size_literals;
@@ -37,7 +37,7 @@ namespace fgl::engine
this->m_vertex_buffer->setDebugName( "Terrain vertex buffer" );
}
vk::CommandBuffer& TerrainSystem::setupSystem( FrameInfo& info )
vk::raii::CommandBuffer& TerrainSystem::setupSystem( FrameInfo& info )
{
auto& command_buffer { info.command_buffer };
m_pipeline->bind( command_buffer );
@@ -52,7 +52,7 @@ namespace fgl::engine
{
ZoneScopedN( "Terrain pass" );
auto& command_buffer { setupSystem( info ) };
TracyVkZone( info.tracy_ctx, command_buffer, "Render terrain" );
TracyVkZone( info.tracy_ctx, *command_buffer, "Render terrain" );
auto [ draw_commands, model_matricies ] =
getDrawCallsFromTree( info.game_objects, info.camera_frustum, IS_VISIBLE | IS_TERRAIN );

View File

@@ -42,7 +42,7 @@ namespace fgl::engine
std::array< std::unique_ptr< ModelMatrixInfoBufferSuballocation >, SwapChain::MAX_FRAMES_IN_FLIGHT >
m_model_matrix_info_buffers {};
vk::CommandBuffer& setupSystem( FrameInfo& info );
vk::raii::CommandBuffer& setupSystem( FrameInfo& info );
void initVertexBuffer( std::uint32_t size )
{
@@ -66,7 +66,7 @@ namespace fgl::engine
inline Buffer& getIndexBuffer() { return *m_index_buffer; }
TerrainSystem( Device& device, VkRenderPass render_pass );
TerrainSystem( Device& device, vk::raii::RenderPass& render_pass );
~TerrainSystem() = default;
void pass( FrameInfo& info );

View File

@@ -4,16 +4,12 @@
#pragma once
#include <concepts>
#include <vulkan/vulkan_raii.hpp>
namespace vk
{
class CommandBuffer;
}
#include <concepts>
namespace fgl::engine
{
struct FrameInfo;
template < typename T >
@@ -34,7 +30,7 @@ namespace fgl::engine
};
{
t.setupSystem( info )
} -> std::same_as< vk::CommandBuffer& >;
} -> std::same_as< vk::raii::CommandBuffer& >;
};
} // namespace fgl::engine

View File

@@ -53,7 +53,7 @@ namespace fgl::engine
void Texture::drawImGui( vk::Extent2D extent )
{
if ( this->m_imgui_set == VK_NULL_HANDLE ) createImGuiSet();
if ( m_imgui_set == VK_NULL_HANDLE ) createImGuiSet();
if ( extent == vk::Extent2D() )
{
@@ -125,7 +125,7 @@ namespace fgl::engine
if ( m_imgui_set != VK_NULL_HANDLE ) ImGui_ImplVulkan_RemoveTexture( m_imgui_set );
}
void Texture::stage( vk::CommandBuffer& cmd )
void Texture::stage( vk::raii::CommandBuffer& cmd )
{
ZoneScoped;
@@ -172,8 +172,10 @@ namespace fgl::engine
region.imageOffset = vk::Offset3D( 0, 0, 0 );
region.imageExtent = vk::Extent3D( m_extent, 1 );
std::vector< vk::BufferImageCopy > regions { region };
cmd.copyBufferToImage(
m_staging->getVkBuffer(), m_image_view->getVkImage(), vk::ImageLayout::eTransferDstOptimal, 1, &region );
m_staging->getVkBuffer(), m_image_view->getVkImage(), vk::ImageLayout::eTransferDstOptimal, regions );
//Transfer back to eGeneral
@@ -227,10 +229,10 @@ namespace fgl::engine
assert( view );
VkImageView vk_view { view->getVkView() };
VkImageView vk_view { **view };
assert( vk_view );
VkSampler vk_sampler { view->getSampler().getVkSampler() };
VkSampler vk_sampler { *( view->getSampler() ) };
m_imgui_set = ImGui_ImplVulkan_AddTexture( vk_sampler, vk_view, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL );
#endif
@@ -252,14 +254,17 @@ namespace fgl::engine
DescriptorSet& Texture::getTextureDescriptorSet()
{
static std::unique_ptr< DescriptorSet > set { nullptr };
static std::optional< vk::DescriptorSetLayout > set_layout { std::nullopt };
static std::optional< vk::raii::DescriptorSetLayout > set_layout { std::nullopt };
if ( set )
return *set;
else
{
set_layout = TextureDescriptorSet::createLayout();
set = std::make_unique< DescriptorSet >( set_layout.value() );
if ( !set_layout.has_value() ) throw std::runtime_error( "No set layout made" );
set = std::make_unique< DescriptorSet >( std::move( set_layout.value() ) );
set->setMaxIDX( 1 );
set->setName( "Texture descriptor set" );
return *set;

View File

@@ -50,7 +50,7 @@ namespace fgl::engine
[[nodiscard]] Texture( const std::filesystem::path& path, const vk::Format format );
void stage( vk::CommandBuffer& cmd ) override;
void stage( vk::raii::CommandBuffer& cmd ) override;
void dropStaging();
public: