diff --git a/shaders/gbuffer.frag b/shaders/gbuffer.frag index 68aed91..a6df606 100644 --- a/shaders/gbuffer.frag +++ b/shaders/gbuffer.frag @@ -21,7 +21,7 @@ layout (set = 0, binding = 0) uniform CameraInfo { layout (set = 1, binding = 0) uniform sampler2D tex[]; #define NEAR_PLANE 0.01f -#define FAR_PLANE 10.0f +#define FAR_PLANE 1000.0f float linearDepth(float depth) { diff --git a/src/engine/EngineContext.cpp b/src/engine/EngineContext.cpp index 8d8aa12..8e24011 100644 --- a/src/engine/EngineContext.cpp +++ b/src/engine/EngineContext.cpp @@ -462,6 +462,8 @@ namespace fgl::engine m_renderer.endFrame(); FrameMark; + using namespace std::chrono_literals; + std::this_thread::sleep_until( new_time + 16ms ); } } @@ -502,7 +504,7 @@ namespace fgl::engine model->syncBuffers( command_buffer ); - int val { 16 }; + int val { 64 }; for ( int x = 0; x < val; ++x ) { @@ -511,7 +513,9 @@ namespace fgl::engine auto sponza = GameObject::createGameObject(); sponza.m_model = model; sponza.m_transform.translation = WorldCoordinate( - 0.0f + ( static_cast< float >( y ) * 30.0f ), 0.0f + ( static_cast< float >( x ) * 20.0f ), 0.0f ); + -900.0f + ( static_cast< float >( y ) * 30.0f ), + -900.0f + ( static_cast< float >( x ) * 20.0f ), + 0.0f ); sponza.m_transform.scale = { 0.007f, 0.007f, 0.007f }; sponza.m_transform.rotation = Rotation( 0.0f, 0.0f, 0.0f ); diff --git a/src/engine/buffers/Buffer.cpp b/src/engine/buffers/Buffer.cpp index fda8b05..e2c5b20 100644 --- a/src/engine/buffers/Buffer.cpp +++ b/src/engine/buffers/Buffer.cpp @@ -284,10 +284,40 @@ namespace fgl::engine return; } - void Buffer::free( fgl::engine::BufferSuballocationHandle& info ) + void Buffer::free( BufferSuballocationHandle& info ) { ZoneScoped; +#ifndef NDEBUG + //Overwrite the bytes with 0xDEADBEEF + + if ( info.mapped ) + { + std::memset( info.mapped, 0xDEADBEEF, info.m_size ); + + vk::MappedMemoryRange range {}; + range.memory = info.buffer.getMemory(); + range.offset = info.m_offset; + + const vk::DeviceSize min_atom_size { Device::getInstance().m_properties.limits.nonCoherentAtomSize }; + const auto size { info.m_size }; + + range.size = align( size, min_atom_size ); + + if ( range.size > info.m_size ) range.size = VK_WHOLE_SIZE; + + if ( Device::getInstance().device().flushMappedMemoryRanges( 1, &range ) != vk::Result::eSuccess ) + { + throw std::runtime_error( "Failed to flush memory" ); + } + + //Flush memory + } + else + throw std::runtime_error( "Unable to give back memory" ); + +#endif + //Find the suballocation auto itter = m_suballocations.find( info.m_offset ); diff --git a/src/engine/buffers/Buffer.hpp b/src/engine/buffers/Buffer.hpp index d6cb175..5bcb0f5 100644 --- a/src/engine/buffers/Buffer.hpp +++ b/src/engine/buffers/Buffer.hpp @@ -125,17 +125,40 @@ namespace fgl::engine public: //! Returns the vulkan buffer handle for this buffer - vk::Buffer getBuffer() const { return m_handle->m_buffer; } + vk::Buffer getBuffer() const + { + assert( m_handle ); + assert( m_handle->m_buffer != VK_NULL_HANDLE ); + + return m_handle->m_buffer; + } //! Returns the vulkan memory handle for this buffer - vk::DeviceMemory getMemory() const { return m_handle->m_alloc_info.deviceMemory; } + vk::DeviceMemory getMemory() const + { + assert( m_handle ); + assert( m_handle->m_alloc_info.deviceMemory != VK_NULL_HANDLE ); + + return m_handle->m_alloc_info.deviceMemory; + } //! Total memory size of the buffer - vk::DeviceSize size() const { return m_handle->m_memory_size; } + vk::DeviceSize size() const + { + assert( m_handle ); + assert( !std::isnan( m_handle->m_memory_size ) ); + + return m_handle->m_memory_size; + } void* map( BufferSuballocationHandle& info ); - bool isMappable() const { return m_handle->m_alloc_info.pMappedData != nullptr; } + bool isMappable() const + { + assert( m_handle ); + + return m_handle->m_alloc_info.pMappedData != nullptr; + } //! Returns a allocation block from this buffer. Block will be aligned with nonUniformBufferOffsetAlignment //! and nonCoherentAtomSize if required (is_uniform_buffer and is_host_visible respectively) diff --git a/src/engine/buffers/BufferSuballocation.cpp b/src/engine/buffers/BufferSuballocation.cpp index 921ec79..a53d240 100644 --- a/src/engine/buffers/BufferSuballocation.cpp +++ b/src/engine/buffers/BufferSuballocation.cpp @@ -21,33 +21,39 @@ namespace fgl::engine other.m_offset = 0; other.m_size = 0; + other.m_handle = nullptr; + return *this; } BufferSuballocation::BufferSuballocation( std::shared_ptr< BufferSuballocationHandle > handle ) : - m_handle( std::move( handle ) ) - { - m_offset = m_handle->m_offset; - m_size = m_handle->m_size; - } + m_handle( std::move( handle ) ), + m_offset( m_handle->m_offset ), + m_size( m_handle->m_size ) + {} - BufferSuballocation::BufferSuballocation( BufferSuballocation&& other ) : m_handle( std::move( other.m_handle ) ) + BufferSuballocation::BufferSuballocation( BufferSuballocation&& other ) : + m_handle( std::move( other.m_handle ) ), + m_offset( m_handle->m_offset ), + m_size( m_handle->m_size ) { - m_offset = m_handle->m_offset; - m_size = m_handle->m_size; - other.m_offset = 0; other.m_size = 0; + + other.m_handle = nullptr; } void* BufferSuballocation::ptr() const { + assert( m_handle != nullptr ); return m_handle->mapped; } void BufferSuballocation::flush( vk::DeviceSize beg, vk::DeviceSize end ) { + assert( m_handle != nullptr ); assert( m_handle->mapped != nullptr && "BufferSuballocationT::flush() called before map()" ); + vk::MappedMemoryRange range {}; range.memory = m_handle->buffer.getMemory(); range.offset = m_offset + beg; @@ -65,21 +71,29 @@ namespace fgl::engine Buffer& BufferSuballocation::getBuffer() const { + assert( m_handle != nullptr ); + return m_handle->buffer; } vk::Buffer BufferSuballocation::getVkBuffer() const { + assert( m_handle != nullptr ); + return m_handle->buffer.getVkBuffer(); } vk::DescriptorBufferInfo BufferSuballocation::descriptorInfo() const { + assert( !std::isnan( m_offset ) ); + assert( !std::isnan( m_size ) ); + return vk::DescriptorBufferInfo( getVkBuffer(), m_offset, m_size ); } SuballocationView BufferSuballocation::view( const vk::DeviceSize offset, const vk::DeviceSize size ) const { + assert( m_handle != nullptr ); assert( offset + size <= m_size && "BufferSuballocation::view() called with offset + size > m_size" ); return { m_handle, offset, size }; diff --git a/src/engine/buffers/BufferSuballocation.hpp b/src/engine/buffers/BufferSuballocation.hpp index 8ddced6..7f8a76a 100644 --- a/src/engine/buffers/BufferSuballocation.hpp +++ b/src/engine/buffers/BufferSuballocation.hpp @@ -21,8 +21,8 @@ namespace fgl::engine std::shared_ptr< BufferSuballocationHandle > m_handle; - vk::DeviceSize m_offset { 0 }; - vk::DeviceSize m_size { 0 }; + vk::DeviceSize m_offset; + vk::DeviceSize m_size; void flush( vk::DeviceSize beg, vk::DeviceSize end ); diff --git a/src/engine/buffers/vector/BufferVector.hpp b/src/engine/buffers/vector/BufferVector.hpp index d718d35..63322fb 100644 --- a/src/engine/buffers/vector/BufferVector.hpp +++ b/src/engine/buffers/vector/BufferVector.hpp @@ -15,8 +15,11 @@ namespace fgl::engine { protected: - std::uint32_t m_count; - std::uint32_t m_stride; + //! Number of items in the vector + std::uint32_t m_count { std::numeric_limits< std::uint32_t >::quiet_NaN() }; + + //! Bytes for each item + std::uint32_t m_stride { std::numeric_limits< std::uint32_t >::quiet_NaN() }; //TODO: Implement spare //std::uint32_t m_spare_count { 0 }; @@ -30,34 +33,41 @@ namespace fgl::engine {} BufferVector( const BufferVector& ) = delete; - BufferVector( BufferVector&& ) = default; - - BufferVector& operator=( BufferVector&& other ) - { - m_count = other.m_count; - m_stride = other.m_stride; - - BufferSuballocation::operator=( std::move( other ) ); - - return *this; - } BufferVector& operator=( const BufferVector& ) = delete; + BufferVector( BufferVector&& ) = default; + + BufferVector& operator=( BufferVector&& other ) = default; + public: //! Returns the offset count from the start of the buffer to the first element [[nodiscard]] std::uint32_t getOffsetCount() const { + assert( !std::isnan( m_count ) ); + assert( !std::isnan( m_stride ) ); return static_cast< std::uint32_t >( this->m_offset / m_stride ); } - [[nodiscard]] std::uint32_t count() const noexcept { return m_count; } + [[nodiscard]] std::uint32_t count() const noexcept + { + assert( !std::isnan( m_count ) ); + return m_count; + } - [[nodiscard]] std::uint32_t stride() const noexcept { return m_stride; } + [[nodiscard]] std::uint32_t stride() const noexcept + { + assert( !std::isnan( m_stride ) ); + return m_stride; + } void resize( const std::uint32_t count ) { + assert( m_handle != nullptr ); + assert( !std::isnan( m_stride ) ); + assert( !std::isnan( m_count ) ); + BufferVector other { this->getBuffer(), count, m_stride }; Device::getInstance().copyBuffer( this->getBuffer(), other.getBuffer(), 0, 0, this->size() ); diff --git a/src/engine/buffers/vector/HostVector.hpp b/src/engine/buffers/vector/HostVector.hpp index a9f7837..b5d145f 100644 --- a/src/engine/buffers/vector/HostVector.hpp +++ b/src/engine/buffers/vector/HostVector.hpp @@ -30,10 +30,7 @@ namespace fgl::engine HostVector& operator=( HostVector&& ) = delete; HostVector( HostVector&& other ) = delete; - HostVector( Buffer& buffer, const std::uint32_t count = 1 ) : BufferVector( buffer, count, sizeof( T ) ) - { - assert( count != 0 && "BufferSuballocationVector::BufferSuballocationVector() called with count == 0" ); - } + HostVector( Buffer& buffer, const std::uint32_t count = 1 ) : BufferVector( buffer, count, sizeof( T ) ) {} HostVector( Buffer& buffer, const std::vector< T >& vec ) : HostVector( buffer, static_cast< std::uint32_t >( vec.size() ) ) @@ -52,6 +49,9 @@ namespace fgl::engine void flushRange( const std::uint32_t start_idx, const std::uint32_t end_idx ) { + if ( this->m_count == 0 ) [[unlikely]] + return; + assert( start_idx < this->m_count && "BufferSuballocationVector::flushRange start_idx index out of bounds" ); assert( end_idx <= this->m_count && "BufferSuballocationVector::flushRange end_idx index out of bounds" ); @@ -62,7 +62,10 @@ namespace fgl::engine ( end_idx - start_idx ) != 0 && "BufferSuballocationVector::flushRange end_idx must be at least +1 from start_idx" ); - BufferSuballocation::flush( start_idx * this->m_stride, ( end_idx - start_idx ) * this->m_stride ); + const auto count { end_idx - start_idx }; + assert( count > 0 && "Count must be larger then 0" ); + + BufferSuballocation::flush( start_idx * this->m_stride, count * this->m_stride ); } HostVector& operator=( const std::vector< T >& vec ) diff --git a/src/engine/constants.hpp b/src/engine/constants.hpp index bd55c55..eac3b5f 100644 --- a/src/engine/constants.hpp +++ b/src/engine/constants.hpp @@ -29,7 +29,7 @@ namespace fgl::engine::constants constexpr float DEFAULT_FLOAT { std::numeric_limits< float >::max() }; constexpr float NEAR_PLANE { 0.1f }; - constexpr float FAR_PLANE { 100.0f }; + constexpr float FAR_PLANE { 1000.0f }; constexpr glm::vec3 CENTER { 0.0f, 0.0f, 0.0f }; constexpr auto EPSILON { std::numeric_limits< float >::epsilon() * 2 }; diff --git a/src/engine/debug/DrawQueue.cpp b/src/engine/debug/DrawQueue.cpp new file mode 100644 index 0000000..b92e361 --- /dev/null +++ b/src/engine/debug/DrawQueue.cpp @@ -0,0 +1,3 @@ +// +// Created by kj16609 on 3/6/24. +// diff --git a/src/engine/debug/DrawQueue.hpp b/src/engine/debug/DrawQueue.hpp new file mode 100644 index 0000000..c8ef9a4 --- /dev/null +++ b/src/engine/debug/DrawQueue.hpp @@ -0,0 +1,25 @@ +// +// Created by kj16609 on 3/6/24. +// + +#pragma once + +namespace fgl::debug +{ + + + + class DrawQueue + { + std::thread draw_consumer; + + //TODO: Replace with ring buffer + std::mutex mtx; + std::queue< > + + + + }; + + +} diff --git a/src/engine/model/Model.cpp b/src/engine/model/Model.cpp index d41949f..8a143b9 100644 --- a/src/engine/model/Model.cpp +++ b/src/engine/model/Model.cpp @@ -98,6 +98,11 @@ namespace fgl::engine { primitive.m_vertex_buffer.stage( cmd_buffer ); primitive.m_index_buffer.stage( cmd_buffer ); + + if ( primitive.m_texture.has_value() ) + { + primitive.m_texture->stage( cmd_buffer ); + } } } diff --git a/src/engine/model/Model.hpp b/src/engine/model/Model.hpp index d4b0d28..c44d0a2 100644 --- a/src/engine/model/Model.hpp +++ b/src/engine/model/Model.hpp @@ -6,21 +6,14 @@ #include -#include #include #include -#include #include #include "Primitive.hpp" -#include "Vertex.hpp" #include "engine/buffers/Buffer.hpp" -#include "engine/buffers/BufferSuballocation.hpp" -#include "engine/buffers/vector/DeviceVector.hpp" -#include "engine/buffers/vector/HostVector.hpp" #include "engine/primitives/boxes/OrientedBoundingBox.hpp" #include "engine/rendering/Device.hpp" -#include "engine/utils.hpp" namespace fgl::engine { @@ -77,7 +70,7 @@ namespace fgl::engine return matrix * m_bounding_box; } - std::vector< ::fgl::engine::Primitive > m_primitives {}; + std::vector< Primitive > m_primitives {}; std::vector< vk::DrawIndexedIndirectCommand > getDrawCommand( const std::uint32_t index ) const; diff --git a/src/engine/model/gltfLoading.cpp b/src/engine/model/gltfLoading.cpp index 42e42c1..668f48c 100644 --- a/src/engine/model/gltfLoading.cpp +++ b/src/engine/model/gltfLoading.cpp @@ -114,13 +114,6 @@ namespace fgl::engine { //TODO: Implement modes - std::cout << "Attributes: \n"; - for ( const auto& thing : primitive.attributes ) - { - std::cout << "\t" << thing.first << "\n"; - } - std::cout << std::endl; - //Load indicies auto& indicies_accessor { model.accessors.at( primitive.indices ) }; diff --git a/src/engine/rendering/Device.cpp b/src/engine/rendering/Device.cpp index 6d1504f..f3ee65e 100644 --- a/src/engine/rendering/Device.cpp +++ b/src/engine/rendering/Device.cpp @@ -19,7 +19,10 @@ static VKAPI_ATTR vk::Bool32 VKAPI_CALL debugCallback( const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, [[maybe_unused]] void* pUserData ) { - std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl; + if ( pCallbackData->flags & VkDebugUtilsMessageSeverityFlagBitsEXT::VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT ) + throw std::runtime_error( pCallbackData->pMessage ); + else + std::cout << pCallbackData->pMessage << std::endl; return VK_FALSE; } @@ -126,10 +129,13 @@ namespace fgl::engine vk::InstanceCreateInfo createInfo {}; createInfo.pApplicationInfo = &appInfo; - auto extensions = getRequiredExtensions(); + auto extensions { getRequiredInstanceExtensions() }; + assert( extensions.size() >= 1 ); createInfo.enabledExtensionCount = static_cast< uint32_t >( extensions.size() ); createInfo.ppEnabledExtensionNames = extensions.data(); + hasGlfwRequiredInstanceExtensions(); + vk::DebugUtilsMessengerCreateInfoEXT debugCreateInfo {}; if ( enableValidationLayers ) { @@ -147,8 +153,6 @@ namespace fgl::engine if ( vk::createInstance( &createInfo, nullptr, &m_instance ) != vk::Result::eSuccess ) throw std::runtime_error( "Failed to create Vulkan instance" ); - - hasGflwRequiredInstanceExtensions(); } void Device::pickPhysicalDevice() @@ -195,6 +199,10 @@ namespace fgl::engine vk::PhysicalDeviceFeatures deviceFeatures = {}; deviceFeatures.samplerAnisotropy = VK_TRUE; deviceFeatures.multiDrawIndirect = VK_TRUE; + deviceFeatures.drawIndirectFirstInstance = VK_TRUE; +#ifndef NDEBUG + //deviceFeatures.robustBufferAccess = VK_TRUE; +#endif vk::PhysicalDeviceDescriptorIndexingFeatures indexing_features {}; indexing_features.setRuntimeDescriptorArray( true ); @@ -214,7 +222,7 @@ namespace fgl::engine //Get device extension list const auto supported_extensions { m_physical_device.enumerateDeviceExtensionProperties() }; - std::cout << "Supported extensions:" << std::endl; + std::cout << "Supported device extensions:" << std::endl; for ( auto& desired_ext : deviceExtensions ) { bool found { false }; @@ -227,6 +235,7 @@ namespace fgl::engine } } std::cout << "\t" << desired_ext << ": " << found << std::endl; + if ( !found ) throw std::runtime_error( "Failed to find required extension" ); } // might not really be necessary anymore because device specific validation layers @@ -241,7 +250,7 @@ namespace fgl::engine createInfo.enabledLayerCount = 0; } - if ( m_physical_device.createDevice( &createInfo, nullptr, &device_ ) != vk::Result::eSuccess ) + if ( auto res = m_physical_device.createDevice( &createInfo, nullptr, &device_ ); res != vk::Result::eSuccess ) { throw std::runtime_error( "failed to create logical device!" ); } @@ -308,7 +317,13 @@ namespace fgl::engine void Device::setupDebugMessenger() { - if ( !enableValidationLayers ) return; + std::cout << "Setting up debug messenger: " << std::endl; + + if ( !enableValidationLayers ) + { + std::cout << "-- Validation disabled" << std::endl; + return; + } pfnVkCreateDebugUtilsMessengerEXT = reinterpret_cast< PFN_vkCreateDebugUtilsMessengerEXT >( m_instance.getProcAddr( "vkCreateDebugUtilsMessengerEXT" ) ); @@ -336,6 +351,8 @@ namespace fgl::engine { throw std::runtime_error( "failed to set up debug messenger!" ); } + + std::cout << "-- Debug callback setup" << std::endl; } bool Device::checkValidationLayerSupport() @@ -364,14 +381,19 @@ namespace fgl::engine return true; } - std::vector< const char* > Device::getRequiredExtensions() + 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 ); @@ -380,29 +402,31 @@ namespace fgl::engine return extensions; } - void Device::hasGflwRequiredInstanceExtensions() + void Device::hasGlfwRequiredInstanceExtensions() { - std::vector< vk::ExtensionProperties > extensions { vk::enumerateInstanceExtensionProperties() }; + std::vector< vk::ExtensionProperties > instance_extensions { vk::enumerateInstanceExtensionProperties() }; - std::cout << "available extensions:" << std::endl; + std::cout << "available instance instance_extensions:" << std::endl; std::unordered_set< std::string > available; - for ( const auto& extension : extensions ) + for ( const auto& extension : instance_extensions ) { std::cout << "\t" << extension.extensionName << std::endl; available.insert( extension.extensionName ); } - std::cout << "required extensions:" << std::endl; - auto requiredExtensions = getRequiredExtensions(); + std::cout << "required instance instance_extensions:" << std::endl; + auto requiredExtensions { getRequiredInstanceExtensions() }; for ( const char* required : requiredExtensions ) { if ( std::find_if( - extensions.begin(), - extensions.end(), + instance_extensions.begin(), + instance_extensions.end(), [ required ]( const vk::ExtensionProperties& prop ) { return std::strcmp( prop.extensionName, required ); } ) - == extensions.end() ) + == instance_extensions.end() ) throw std::runtime_error( "Missing required glfw extension" ); + else + std::cout << required << std::endl; } } diff --git a/src/engine/rendering/Device.hpp b/src/engine/rendering/Device.hpp index 1d5b8b5..96faf44 100644 --- a/src/engine/rendering/Device.hpp +++ b/src/engine/rendering/Device.hpp @@ -45,9 +45,9 @@ namespace fgl::engine vk::Queue graphicsQueue_ { VK_NULL_HANDLE }; vk::Queue presentQueue_ { VK_NULL_HANDLE }; - const std::vector< const char* > validationLayers { "VK_LAYER_KHRONOS_validation" }; - const 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 }; void copyBuffer( vk::Buffer dst, @@ -94,11 +94,11 @@ namespace fgl::engine // helper functions bool isDeviceSuitable( vk::PhysicalDevice device ); - std::vector< const char* > getRequiredExtensions(); + std::vector< const char* > getRequiredInstanceExtensions(); bool checkValidationLayerSupport(); QueueFamilyIndices findQueueFamilies( vk::PhysicalDevice device ); void populateDebugMessengerCreateInfo( vk::DebugUtilsMessengerCreateInfoEXT& createInfo ); - void hasGflwRequiredInstanceExtensions(); + void hasGlfwRequiredInstanceExtensions(); bool checkDeviceExtensionSupport( vk::PhysicalDevice device ); SwapChainSupportDetails querySwapChainSupport( vk::PhysicalDevice device ); @@ -106,10 +106,10 @@ namespace fgl::engine public: -#ifdef NDEBUG - const bool enableValidationLayers = false; +#ifndef NDEBUG + const bool enableValidationLayers { true }; #else - const bool enableValidationLayers = true; + const bool enableValidationLayers { false }; #endif vk::Result setDebugUtilsObjectName( const vk::DebugUtilsObjectNameInfoEXT& nameInfo ); diff --git a/src/engine/rendering/Subpass.hpp b/src/engine/rendering/Subpass.hpp index 003d2c2..6f2935f 100644 --- a/src/engine/rendering/Subpass.hpp +++ b/src/engine/rendering/Subpass.hpp @@ -136,7 +136,7 @@ namespace fgl::engine } template < is_subpass SrcT > - void registerDependency( + void registerDependencyFrom( SrcT& parent, const vk::AccessFlags src_access_flags, const vk::PipelineStageFlags src_stage_flags, @@ -154,7 +154,7 @@ namespace fgl::engine dependency_flags ); } - void registerExternalDependency( + void registerDependencyFromExternal( const vk::AccessFlags access_flags, const vk::PipelineStageFlags stage_flags, const vk::DependencyFlags dependency_flags = {} ) @@ -164,10 +164,27 @@ namespace fgl::engine this->index, access_flags, stage_flags, - vk::AccessFlags( 0 ), + access_flags, stage_flags, dependency_flags ); } + + void registerDependencyToExternal( + const vk::AccessFlags src_access_flags, + const vk::PipelineStageFlags src_stage_flags, + const vk::AccessFlags dst_access_flags, + const vk::PipelineStageFlags dst_stage_flags, + const vk::DependencyFlags dependency_flags ) + { + registerDependency( + this->getIndex(), + VK_SUBPASS_EXTERNAL, + src_access_flags, + src_stage_flags, + dst_access_flags, + dst_stage_flags, + dependency_flags ); + } }; } // namespace fgl::engine diff --git a/src/engine/rendering/SwapChain.cpp b/src/engine/rendering/SwapChain.cpp index e3edc6b..e847bd1 100644 --- a/src/engine/rendering/SwapChain.cpp +++ b/src/engine/rendering/SwapChain.cpp @@ -223,8 +223,10 @@ namespace fgl::engine for ( std::uint64_t i = 0; i < swap_chain_images.size(); i++ ) { - m_swap_chain_images - .emplace_back( extent, surfaceFormat.format, swap_chain_images[ i ], createInfo.imageUsage ); + auto& itter = + m_swap_chain_images + .emplace_back( extent, surfaceFormat.format, swap_chain_images[ i ], createInfo.imageUsage ); + itter.setName( "Swapchain image: " + std::to_string( i ) ); } m_swap_chain_format = surfaceFormat.format; @@ -305,11 +307,11 @@ namespace fgl::engine 0, depthAttachment, colorAttachment, g_buffer_position, g_buffer_normal, g_buffer_albedo }; - g_buffer_subpass.registerExternalDependency( + g_buffer_subpass.registerDependencyFromExternal( vk::AccessFlagBits::eDepthStencilAttachmentWrite, vk::PipelineStageFlagBits::eEarlyFragmentTests | vk::PipelineStageFlagBits::eLateFragmentTests ); - g_buffer_subpass.registerExternalDependency( + g_buffer_subpass.registerDependencyFromExternal( vk::AccessFlagBits::eColorAttachmentWrite, vk::PipelineStageFlagBits::eColorAttachmentOutput ); Subpass< @@ -323,18 +325,7 @@ namespace fgl::engine 1, depthAttachment, colorAttachment, g_buffer_position, g_buffer_normal, g_buffer_albedo }; - /* - // This dependency transitions the input attachment from color attachment to input attachment read - dependencies[ 2 ].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - dependencies[ 2 ].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - - dependencies[ 2 ].dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; - dependencies[ 2 ].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - - dependencies[ 2 ].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; - */ - - present_subpass.registerDependency( + present_subpass.registerDependencyFrom( g_buffer_subpass, vk::AccessFlagBits::eColorAttachmentWrite, vk::PipelineStageFlagBits::eColorAttachmentOutput, @@ -342,9 +333,7 @@ namespace fgl::engine vk::PipelineStageFlagBits::eFragmentShader, vk::DependencyFlagBits::eByRegion ); - present_subpass.registerDependency( - present_subpass.getIndex(), - VK_SUBPASS_EXTERNAL, + present_subpass.registerDependencyToExternal( vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite, vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::AccessFlagBits::eMemoryRead, diff --git a/src/engine/systems/CullingSystem.cpp b/src/engine/systems/CullingSystem.cpp index 7f437cb..dbbaf08 100644 --- a/src/engine/systems/CullingSystem.cpp +++ b/src/engine/systems/CullingSystem.cpp @@ -39,7 +39,7 @@ namespace fgl::engine if ( obj.m_is_visible ) { //Draw the bounding box for debug - debug::world::drawBoundingBox( model_bounding_box ); + //debug::world::drawBoundingBox( model_bounding_box ); } } } diff --git a/src/engine/systems/EntityRendererSystem.cpp b/src/engine/systems/EntityRendererSystem.cpp index decce16..45e2c11 100644 --- a/src/engine/systems/EntityRendererSystem.cpp +++ b/src/engine/systems/EntityRendererSystem.cpp @@ -4,6 +4,7 @@ #include "EntityRendererSystem.hpp" +#include #include #include #include @@ -14,6 +15,31 @@ #include "engine/literals/size.hpp" #include "engine/tree/octtree/OctTreeNode.hpp" +namespace fgl::engine +{ + + // + using DrawKey = std::pair< TextureID, vk::DeviceSize >; +} // namespace fgl::engine + +namespace std +{ + template <> + struct hash< fgl::engine::DrawKey > + { + size_t operator()( const fgl::engine::DrawKey& key ) const + { + const auto id_hash { std::hash< fgl::engine::TextureID >()( key.first ) }; + const auto offset_hash { std::hash< vk::DeviceSize >()( key.second ) }; + + size_t seed { 0 }; + fgl::engine::hashCombine( seed, id_hash, offset_hash ); + return seed; + } + }; + +} // namespace std + namespace fgl::engine { std::unique_ptr< Buffer > m_global_draw_parameter_buffer { nullptr }; @@ -81,53 +107,59 @@ namespace fgl::engine m_pipeline->bindDescriptor( command_buffer, 0, info.global_descriptor_set ); m_pipeline->bindDescriptor( command_buffer, 1, Texture::getTextureDescriptorSet() ); - std::set< DrawPair > draw_pairs; + std::unordered_map< DrawKey, DrawPair > draw_pairs; std::uint64_t tri_counter { 0 }; + std::uint64_t object_counter { 0 }; + std::uint64_t primitive_counter { 0 }; for ( auto* node : info.game_objects.getAllLeafsInFrustum( info.camera_frustum ) ) { + ZoneScopedN( "Process leaf" ); for ( const auto& obj : *node ) { + ZoneScopedN( "Process object" ); if ( obj.m_model == nullptr ) continue; if ( !obj.m_is_visible ) continue; + ++object_counter; + for ( const auto& primitive : obj.m_model->m_primitives ) { + ++primitive_counter; tri_counter += ( primitive.m_index_buffer.count() / 3 ); const ModelMatrixInfo matrix_info { .model_matrix = obj.m_transform.mat4(), .texture_idx = primitive.m_texture->getID() }; - //.normal_matrix = obj.transform.normalMatrix() }; - vk::DrawIndexedIndirectCommand cmd; + const auto key { + std::make_pair( primitive.m_texture->getID(), primitive.m_index_buffer.getOffset() ) + }; - cmd.firstIndex = primitive.m_index_buffer.getOffsetCount(); - cmd.indexCount = primitive.m_index_buffer.count(); - - cmd.vertexOffset = primitive.m_vertex_buffer.getOffsetCount(); - - cmd.instanceCount = 1; - - auto itter = std::find( - draw_pairs.begin(), - draw_pairs.end(), - std::make_pair( cmd, std::vector< ModelMatrixInfo >() ) ); - - if ( itter != draw_pairs.end() ) + if ( auto itter = draw_pairs.find( key ); itter != draw_pairs.end() ) { //Draw command for this mesh already exists. Simply add a count to it - auto [ existing_cmd, model_matrix ] = *itter; + auto& [ itter_key, pair ] = *itter; + auto& [ existing_cmd, model_matrix ] = pair; - draw_pairs.erase( itter ); existing_cmd.instanceCount++; model_matrix.emplace_back( matrix_info ); - draw_pairs.emplace( existing_cmd, std::move( model_matrix ) ); } else { - draw_pairs.emplace( cmd, std::vector< ModelMatrixInfo > { matrix_info } ); + vk::DrawIndexedIndirectCommand cmd {}; + + cmd.firstIndex = primitive.m_index_buffer.getOffsetCount(); + cmd.indexCount = primitive.m_index_buffer.count(); + + cmd.vertexOffset = static_cast< int32_t >( primitive.m_vertex_buffer.getOffsetCount() ); + + cmd.instanceCount = 1; + + std::vector< ModelMatrixInfo > matrix_infos {}; + matrix_infos.reserve( 1024 ); + draw_pairs.emplace( key, std::make_pair( cmd, std::move( matrix_infos ) ) ); } } } @@ -135,6 +167,8 @@ namespace fgl::engine #if ENABLE_IMGUI ImGui::Text( "Tris: %lu", tri_counter ); + ImGui::Text( "Models: %lu", object_counter ); + ImGui::Text( "Primitives: %lu", primitive_counter ); #endif if ( draw_pairs.empty() ) @@ -147,12 +181,15 @@ namespace fgl::engine std::vector< vk::DrawIndexedIndirectCommand > draw_commands; std::vector< ModelMatrixInfo > model_matrices; + draw_commands.reserve( draw_pairs.size() ); + model_matrices.reserve( draw_pairs.size() * 2 ); + TracyCZoneN( filter_zone_TRACY, "Reorganize draw commands", true ); - for ( auto& itter : draw_pairs ) + for ( auto& [ key, pair ] : draw_pairs ) { - auto cmd { itter.first }; + auto cmd { pair.first }; cmd.firstInstance = static_cast< std::uint32_t >( model_matrices.size() ); - auto matricies { std::move( itter.second ) }; + auto matricies { std::move( pair.second ) }; draw_commands.emplace_back( cmd ); model_matrices.insert( model_matrices.end(), matricies.begin(), matricies.end() ); @@ -183,15 +220,19 @@ namespace fgl::engine model_matrix_info_buffer->flush(); - auto& model_matricies_suballoc { model_matrix_info_buffer }; - auto& draw_params { draw_parameter_buffer }; + const auto& model_matricies_suballoc { model_matrix_info_buffer }; + const auto& draw_params { draw_parameter_buffer }; - std::vector< vk::Buffer > vertex_buffers { m_vertex_buffer->getVkBuffer(), - model_matricies_suballoc->getVkBuffer() }; + const std::vector< vk::Buffer > vertex_buffers { m_vertex_buffer->getVkBuffer(), + model_matricies_suballoc->getVkBuffer() }; command_buffer.bindVertexBuffers( 0, vertex_buffers, { 0, model_matricies_suballoc->getOffset() } ); command_buffer.bindIndexBuffer( m_index_buffer->getVkBuffer(), 0, vk::IndexType::eUint32 ); +#if ENABLE_IMGUI + ImGui::Text( "Indirect draws: %lu", static_cast< std::size_t >( draw_params->count() ) ); +#endif + command_buffer.drawIndexedIndirect( draw_params->getVkBuffer(), draw_params->getOffset(), draw_params->count(), draw_params->stride() ); diff --git a/src/engine/texture/Texture.cpp b/src/engine/texture/Texture.cpp index 0dc4811..748ddb6 100644 --- a/src/engine/texture/Texture.cpp +++ b/src/engine/texture/Texture.cpp @@ -119,7 +119,7 @@ namespace fgl::engine barrier.srcAccessMask = {}; barrier.dstAccessMask = vk::AccessFlagBits::eTransferWrite; - std::vector< vk::ImageMemoryBarrier > barriers_to { barrier }; + const std::vector< vk::ImageMemoryBarrier > barriers_to { barrier }; cmd.pipelineBarrier( vk::PipelineStageFlagBits::eTopOfPipe, @@ -153,13 +153,13 @@ namespace fgl::engine vk::ImageMemoryBarrier barrier_from {}; barrier_from.oldLayout = barrier.newLayout; - barrier_from.newLayout = vk::ImageLayout::eGeneral; + barrier_from.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal; barrier_from.image = m_handle->m_image_view->getVkImage(); barrier_from.subresourceRange = range; barrier_from.srcAccessMask = vk::AccessFlagBits::eTransferWrite; barrier_from.dstAccessMask = vk::AccessFlagBits::eShaderRead; - std::vector< vk::ImageMemoryBarrier > barriers_from { barrier_from }; + const std::vector< vk::ImageMemoryBarrier > barriers_from { barrier_from }; cmd.pipelineBarrier( vk::PipelineStageFlagBits::eTransfer, diff --git a/src/engine/texture/Texture.hpp b/src/engine/texture/Texture.hpp index 73eac22..517a523 100644 --- a/src/engine/texture/Texture.hpp +++ b/src/engine/texture/Texture.hpp @@ -19,6 +19,8 @@ namespace fgl::engine class Texture { std::shared_ptr< TextureHandle > m_handle; + //! Has this texture been submitted to the GPU? + bool submitte_to_gpu { false }; Texture( std::tuple< std::vector< unsigned char >, int, int, int > ); Texture( std::shared_ptr< TextureHandle > handle ); diff --git a/src/engine/tree/octtree/OctTreeNode.cpp b/src/engine/tree/octtree/OctTreeNode.cpp index 10a6896..c1bbb28 100644 --- a/src/engine/tree/octtree/OctTreeNode.cpp +++ b/src/engine/tree/octtree/OctTreeNode.cpp @@ -15,6 +15,7 @@ namespace fgl::engine std::vector< NodeLeaf* > OctTreeNode::getAllLeafsInFrustum( const Frustum< CoordinateSpace::World >& frustum ) { + ZoneScoped; std::vector< NodeLeaf* > leafs {}; //Check if we are inside of the frustum. @@ -29,7 +30,7 @@ namespace fgl::engine { const auto ret { node_array[ LEFT ][ FORWARD ][ TOP ]->getAllLeafsInFrustum( frustum ) }; - leafs.insert( leafs.end(), ret.begin(), ret.end() ); + leafs = std::move( ret ); } { const auto ret { node_array[ LEFT ][ FORWARD ][ BOTTOM ]->getAllLeafsInFrustum( frustum ) }; @@ -66,9 +67,10 @@ namespace fgl::engine } case 1: // NodeLeaf { + leafs.reserve( 4096 ); leafs.emplace_back( &std::get< NodeLeaf >( m_node_data ) ); - debug::world::drawBoundingBox( m_bounds ); + // debug::world::drawBoundingBox( m_bounds ); return leafs; } @@ -84,12 +86,12 @@ namespace fgl::engine m_bounds( center, span ), m_node_data( NodeLeaf() ), m_parent( parent ) - {} + { + std::get< NodeLeaf >( m_node_data ).reserve( MAX_NODES_IN_LEAF ); + } void OctTreeNode::split( int depth ) { - std::cout << "Splitting node: " << glm::to_string( this->m_bounds.getPosition().vec() ) << std::endl; - if ( std::holds_alternative< NodeArray >( m_node_data ) ) return; auto& game_objects { std::get< NodeLeaf >( m_node_data ) }; @@ -192,6 +194,7 @@ namespace fgl::engine OctTreeNode* OctTreeNode::findID( const GameObject::ID id ) { + ZoneScoped; if ( std::holds_alternative< NodeLeaf >( this->m_node_data ) ) { //We are the last node. Check if we have the ID @@ -264,6 +267,7 @@ namespace fgl::engine std::vector< NodeLeaf* > OctTreeNode::getAllLeafs() { + ZoneScoped; std::vector< NodeLeaf* > objects {}; if ( std::holds_alternative< NodeLeaf >( m_node_data ) )