From 0cf659916e823fe78ef10e5a1611e8b23a415aba Mon Sep 17 00:00:00 2001 From: Sylvain Doremus Date: Mon, 12 Jun 2023 00:45:34 +0200 Subject: [PATCH] Now recording all frame passes commands to a command buffer held by the runnable graph. --- include/RenderGraph/FrameGraph.hpp | 10 +- include/RenderGraph/GraphContext.hpp | 27 ++++ include/RenderGraph/RunnableGraph.hpp | 39 +++++- include/RenderGraph/RunnablePass.hpp | 23 +++- .../RunnablePasses/RenderPassHolder.hpp | 4 +- source/RenderGraph/RunnableGraph.cpp | 125 +++++++++++++----- source/RenderGraph/RunnablePass.cpp | 42 +++--- .../RunnablePasses/PipelineHolder.cpp | 12 +- .../RunnablePasses/RenderPassHolder.cpp | 27 ++-- .../RunnablePasses/RenderQuadHolder.cpp | 5 + 10 files changed, 232 insertions(+), 82 deletions(-) diff --git a/include/RenderGraph/FrameGraph.hpp b/include/RenderGraph/FrameGraph.hpp index 3a77f71..62ed323 100644 --- a/include/RenderGraph/FrameGraph.hpp +++ b/include/RenderGraph/FrameGraph.hpp @@ -100,27 +100,27 @@ namespace crg , VkImageSubresourceRange range )const; CRG_API LayoutState getOutputLayoutState( ImageViewId view )const; - ResourceHandler & getHandler() + ResourceHandler & getHandler()noexcept { return m_handler; } - std::string const & getName()const + std::string const & getName()const noexcept { return m_name; } - FrameGraphArray const & getDependencies()const + FrameGraphArray const & getDependencies()const noexcept { return m_depends; } - RecordContext const & getFinalStates()const + RecordContext const & getFinalStates()const noexcept { return m_finalState; } - FramePassGroup & getDefaultGroup()const + FramePassGroup & getDefaultGroup()const noexcept { return *m_defaultGroup; } diff --git a/include/RenderGraph/GraphContext.hpp b/include/RenderGraph/GraphContext.hpp index e903c8a..14dc0fd 100644 --- a/include/RenderGraph/GraphContext.hpp +++ b/include/RenderGraph/GraphContext.hpp @@ -446,6 +446,32 @@ namespace crg std::array< float, 4 > colour; }; + struct DeletionQueue + { + using CDtorFunc = void( GraphContext & ); + using DtorFunc = std::function< CDtorFunc >; + using DtorFuncArray = std::vector< DtorFunc >; + + public: + void push( DtorFunc func ) + { + m_toDelete.push_back( func ); + } + + void clear( GraphContext & context ) + { + DtorFuncArray tmp{ std::move( m_toDelete ) }; + + for ( auto func : tmp ) + { + func( context ); + } + } + + private: + DtorFuncArray m_toDelete; + }; + struct GraphContext { CRG_API GraphContext( GraphContext const & ) = delete; @@ -468,6 +494,7 @@ namespace crg VkPhysicalDeviceProperties properties{}; VkPhysicalDeviceFeatures features{}; bool separateDepthStencilLayouts; + DeletionQueue delQueue; #define DECL_vkFunction( name )\ PFN_vk##name vk##name{ nullptr } diff --git a/include/RenderGraph/RunnableGraph.hpp b/include/RenderGraph/RunnableGraph.hpp index d506eaf..c795ba1 100644 --- a/include/RenderGraph/RunnableGraph.hpp +++ b/include/RenderGraph/RunnableGraph.hpp @@ -58,6 +58,7 @@ namespace crg , GraphNodePtrArray nodes , RootNode rootNode , GraphContext & context ); + CRG_API ~RunnableGraph()noexcept; CRG_API void record(); @@ -85,41 +86,61 @@ namespace crg , ImageViewId view )const; CRG_API LayoutState getOutputLayoutState( ImageViewId view )const; - ConstGraphAdjacentNode getGraph()const + ConstGraphAdjacentNode getGraph()const noexcept { return &m_rootNode; } - std::string const & getName()const + std::string const & getName()const noexcept { return m_graph.getName(); } - AttachmentTransitions const & getTransitions()const + AttachmentTransitions const & getTransitions()const noexcept { return m_transitions; } - VkCommandPool getCommandPool()const + VkCommandPool getCommandPool()const noexcept { return m_commandPool.object; } - VkQueryPool getTimerQueryPool()const + VkQueryPool getTimerQueryPool()const noexcept { return m_timerQueries.object; } - uint32_t & getTimerQueryOffset() + uint32_t & getTimerQueryOffset()noexcept { return m_timerQueryOffset; } - ContextResourcesCache & getResources() + ContextResourcesCache & getResources()noexcept { return m_resources; } + Fence & getFence()noexcept + { + return m_fence; + } + + Fence const & getFence()const noexcept + { + return m_fence; + } + + FramePassTimer const & getTimer()const + { + return m_timer; + } + + FramePassTimer & getTimer() + { + return m_timer; + } + private: FrameGraph & m_graph; GraphContext & m_context; @@ -134,5 +155,9 @@ namespace crg ContextObjectT< VkCommandPool > m_commandPool; std::vector< RunnablePassPtr > m_passes; RecordContext::GraphIndexMap m_states; + VkCommandBuffer m_commandBuffer{}; + VkSemaphore m_semaphore{}; + Fence m_fence; + FramePassTimer m_timer; }; } diff --git a/include/RenderGraph/RunnablePass.hpp b/include/RenderGraph/RunnablePass.hpp index 204b549..7b9a29d 100644 --- a/include/RenderGraph/RunnablePass.hpp +++ b/include/RenderGraph/RunnablePass.hpp @@ -197,25 +197,37 @@ namespace crg CRG_API virtual ~RunnablePass(); /** *\brief - * Initialises the descriptor set. + * Initialises the pass GPU data for given index. + *\param[in] passIndex + * The index of the pass to initialise. */ CRG_API void initialise( uint32_t passIndex ); /** *\brief * Records the pass commands into its command buffer. - *\param[in] index - * The pass index. + *\param[in,out] context + * Stores the states. */ CRG_API void recordAll( RecordContext & context ); /** *\brief * Records the pass commands into its command buffer. - *\param[in] index - * The pass index. + *\param[in,out] context + * Stores the states. */ CRG_API uint32_t recordCurrent( RecordContext & context ); /** *\brief + * Records the pass commands into the given command buffer. + *\param[in,out] context + * Stores the states. + *\param[in] commandBuffer + * Receives the commands. + */ + CRG_API uint32_t recordCurrentInto( RecordContext & context + , VkCommandBuffer commandBuffer ); + /** + *\brief * Re-records the pass commands into its command buffer. */ CRG_API uint32_t reRecordCurrent(); @@ -248,7 +260,6 @@ namespace crg * Resets the command buffer to initial state. */ CRG_API void resetCommandBuffer( uint32_t passIndex ); - CRG_API void resetCommandBuffers(); CRG_API void setToReset( uint32_t passIndex ); LayoutState getLayoutState( crg::ImageViewId view )const diff --git a/include/RenderGraph/RunnablePasses/RenderPassHolder.hpp b/include/RenderGraph/RunnablePasses/RenderPassHolder.hpp index 8e355f1..e808b07 100644 --- a/include/RenderGraph/RunnablePasses/RenderPassHolder.hpp +++ b/include/RenderGraph/RunnablePasses/RenderPassHolder.hpp @@ -81,6 +81,8 @@ namespace crg std::vector< Attachment const * > attachments; std::vector< VkClearValue > clearValues; std::vector< Entry > attaches; + PipelineState previousState; + PipelineState nextState; void cleanup( crg::GraphContext & context ); }; @@ -94,8 +96,6 @@ namespace crg VkPipelineColorBlendAttachmentStateArray m_blendAttachs; uint32_t m_layers{}; uint32_t m_index{}; - PipelineState m_srcState; - PipelineState m_dstState; uint32_t m_count{ 1u }; }; } diff --git a/source/RenderGraph/RunnableGraph.cpp b/source/RenderGraph/RunnableGraph.cpp index f70dd02..89570a8 100644 --- a/source/RenderGraph/RunnableGraph.cpp +++ b/source/RenderGraph/RunnableGraph.cpp @@ -164,7 +164,7 @@ namespace crg , m_nodes{ std::move( nodes ) } , m_rootNode{ std::move( rootNode ) } , m_timerQueries{ m_context - , createQueryPool( m_context, m_graph.getName() + "TimerQueries", uint32_t( m_nodes.size() * 2u ) ) + , createQueryPool( m_context, m_graph.getName() + "TimerQueries", uint32_t( ( m_nodes.size() + 1u ) * 2u ) ) , []( GraphContext & ctx, VkQueryPool & object ) { crgUnregisterObject( ctx, object ); @@ -179,7 +179,43 @@ namespace crg ctx.vkDestroyCommandPool( ctx.device, object, ctx.allocator ); object = {}; } } + , m_fence{ m_context + , graph.getName() + "/Graph" + , { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, nullptr, VK_FENCE_CREATE_SIGNALED_BIT } } + , m_timer{ context, graph.getName() + "/Graph", getTimerQueryPool(), getTimerQueryOffset() } { + if ( m_context.device ) + { + auto name = m_graph.getName() + "/Graph"; + VkSemaphoreCreateInfo createInfo{ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO + , nullptr + , 0u }; + auto res = context.vkCreateSemaphore( m_context.device + , &createInfo + , m_context.allocator + , &m_semaphore ); + checkVkResult( res, name + " - Semaphore creation" ); + crgRegisterObject( m_context, name, m_semaphore ); + + VkCommandBufferAllocateInfo allocateInfo{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO + , nullptr + , getCommandPool() + , VK_COMMAND_BUFFER_LEVEL_PRIMARY + , 1u }; + res = m_context.vkAllocateCommandBuffers( m_context.device + , &allocateInfo + , &m_commandBuffer ); + checkVkResult( res, name + " - CommandBuffer allocation" ); + crgRegisterObject( m_context, name, m_commandBuffer ); + + VkCommandBufferBeginInfo beginInfo{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO + , nullptr + , VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT + , nullptr }; + m_context.vkBeginCommandBuffer( m_commandBuffer, &beginInfo ); + m_context.vkEndCommandBuffer( m_commandBuffer ); + } + Logger::logDebug( m_graph.getName() + " - Initialising resources" ); for ( auto & img : m_graph.m_images ) @@ -215,8 +251,32 @@ namespace crg } } + RunnableGraph::~RunnableGraph()noexcept + { + if ( m_context.device ) + { + if ( m_semaphore ) + { + crgUnregisterObject( m_context, m_semaphore ); + m_context.vkDestroySemaphore( m_context.device + , m_semaphore + , m_context.allocator ); + } + + if ( m_commandBuffer ) + { + crgUnregisterObject( m_context, m_commandBuffer ); + m_context.vkFreeCommandBuffers( m_context.device + , getCommandPool() + , 1u + , &m_commandBuffer ); + } + } + } + void RunnableGraph::record() { + auto block( m_timer.start() ); m_states.clear(); RecordContext recordContext{ m_resources }; @@ -237,6 +297,14 @@ namespace crg recordContext.setNextPipelineState( ( *currPass )->getPipelineState() , ( *currPass )->getImageLayouts() ); auto nextPass = std::next( currPass ); + m_fence.wait( 0xFFFFFFFFFFFFFFFFull ); + m_context.vkResetCommandBuffer( m_commandBuffer, 0u ); + VkCommandBufferBeginInfo beginInfo{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO + , nullptr + , VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT + , nullptr }; + m_context.vkBeginCommandBuffer( m_commandBuffer, &beginInfo ); + m_timer.beginPass( m_commandBuffer ); while ( currPass != m_passes.end() ) { @@ -261,9 +329,12 @@ namespace crg , {} ); } - *it = pass->recordCurrent( recordContext ); + *it = pass->recordCurrentInto( recordContext, m_commandBuffer ); ++it; } + + m_timer.endPass( m_commandBuffer ); + m_context.vkEndCommandBuffer( m_commandBuffer ); } m_graph.registerFinalState( recordContext ); @@ -287,34 +358,28 @@ namespace crg SemaphoreWaitArray RunnableGraph::run( SemaphoreWaitArray const & toWait , VkQueue queue ) { - RecordContext::GraphIndexMap states; - auto it = states.emplace( &m_graph, RecordContext::PassIndexArray{} ).first; - it->second.reserve( m_passes.size() ); - - for ( auto & pass : m_passes ) - { - it->second.push_back( pass->getIndex() ); - } - - for ( auto & dependency : m_graph.getDependencies() ) - { - states.emplace( dependency - , dependency->getFinalStates().getIndexState() ); - } - - if ( m_states != states ) - { - record(); - } - - auto result = toWait; - - for ( auto & pass : m_passes ) - { - result = pass->run( result, queue ); - } - - return result; + record(); + std::vector< VkSemaphore > semaphores; + std::vector< VkPipelineStageFlags > dstStageMasks; + convert( toWait, semaphores, dstStageMasks ); + m_timer.notifyPassRender(); + VkSubmitInfo submitInfo{ VK_STRUCTURE_TYPE_SUBMIT_INFO + , nullptr + , uint32_t( semaphores.size() ) + , semaphores.data() + , dstStageMasks.data() + , 1u + , &m_commandBuffer + , 1u + , &m_semaphore }; + m_fence.reset(); + m_context.delQueue.clear( m_context ); + m_context.vkQueueSubmit( queue + , 1u + , &submitInfo + , m_fence ); + return { SemaphoreWait{ m_semaphore + , m_graph.getFinalStates().getCurrPipelineState().pipelineStage } }; } ImageViewId RunnableGraph::createView( ImageViewData const & view ) diff --git a/source/RenderGraph/RunnablePass.cpp b/source/RenderGraph/RunnablePass.cpp index b26c6e3..0eeb6a5 100644 --- a/source/RenderGraph/RunnablePass.cpp +++ b/source/RenderGraph/RunnablePass.cpp @@ -343,19 +343,33 @@ namespace crg return isEnabled() ? index : ~( 0u ); } + uint32_t RunnablePass::recordCurrentInto( RecordContext & context + , VkCommandBuffer commandBuffer ) + { + auto index = m_callbacks.getPassIndex(); + recordInto( commandBuffer + , index + , context ); + return isEnabled() ? index : ~( 0u ); + } + uint32_t RunnablePass::reRecordCurrent() { assert( m_ruConfig.resettable ); auto index = m_callbacks.getPassIndex(); - auto it = m_passContexts.find( index ); + auto & pass = m_passes[index]; - if ( it != m_passContexts.end() ) + if ( pass.commandBuffer.commandBuffer ) { - auto context = it->second; - auto & pass = m_passes[index]; - recordOne( pass.commandBuffer - , index - , context ); + auto it = m_passContexts.find( index ); + + if ( it != m_passContexts.end() ) + { + auto context = it->second; + recordOne( pass.commandBuffer + , index + , context ); + } } return isEnabled() ? index : ~( 0u ); @@ -401,9 +415,7 @@ namespace crg , VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT , nullptr }; m_context.vkBeginCommandBuffer( enabled.commandBuffer, &beginInfo ); - recordInto( enabled.commandBuffer - , index - , context ); + recordInto( enabled.commandBuffer, index, context ); m_context.vkEndCommandBuffer( enabled.commandBuffer ); } @@ -567,24 +579,16 @@ namespace crg assert( m_passes.size() > passIndex ); auto & pass = m_passes[passIndex]; - pass.fence.wait( 0xFFFFFFFFFFFFFFFFull ); if ( pass.commandBuffer.commandBuffer ) { + pass.fence.wait( 0xFFFFFFFFFFFFFFFFull ); pass.commandBuffer.recorded = false; m_context.vkResetCommandBuffer( pass.commandBuffer.commandBuffer , VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT ); } } - void RunnablePass::resetCommandBuffers() - { - for ( uint32_t i = 0u; i < uint32_t( m_passes.size() ); ++i ) - { - resetCommandBuffer( i ); - } - } - void RunnablePass::setToReset( uint32_t passIndex ) { if ( m_ruConfig.resettable ) diff --git a/source/RenderGraph/RunnablePasses/PipelineHolder.cpp b/source/RenderGraph/RunnablePasses/PipelineHolder.cpp index e5aa4b0..5aea249 100644 --- a/source/RenderGraph/RunnablePasses/PipelineHolder.cpp +++ b/source/RenderGraph/RunnablePasses/PipelineHolder.cpp @@ -206,10 +206,14 @@ namespace crg if ( m_pipelines[index] ) { - crgUnregisterObject( m_context, m_pipelines[index] ); - m_context.vkDestroyPipeline( m_context.device - , m_pipelines[index] - , m_context.allocator ); + auto pipeline = m_pipelines[index]; + m_context.delQueue.push( [pipeline]( GraphContext & context ) + { + crgUnregisterObject( context, pipeline ); + context.vkDestroyPipeline( context.device + , pipeline + , context.allocator ); + } ); m_pipelines[index] = {}; } diff --git a/source/RenderGraph/RunnablePasses/RenderPassHolder.cpp b/source/RenderGraph/RunnablePasses/RenderPassHolder.cpp index 6f9a038..6475aff 100644 --- a/source/RenderGraph/RunnablePasses/RenderPassHolder.cpp +++ b/source/RenderGraph/RunnablePasses/RenderPassHolder.cpp @@ -81,6 +81,13 @@ namespace crg , separateDepthStencilLayouts ); } + static bool operator!=( LayoutState const & lhs, LayoutState const & rhs ) + { + return lhs.layout != rhs.layout + || lhs.state.access != rhs.state.access + || ( lhs.state.access != 0 && lhs.state.pipelineStage != rhs.state.pipelineStage ); + } + static bool checkAttaches( RecordContext const & context , std::vector< RenderPassHolder::Entry > const & attaches ) { @@ -88,7 +95,7 @@ namespace crg , attaches.end() , [&context]( RenderPassHolder::Entry const & lookup ) { - return context.getLayoutState( lookup.view ).layout != lookup.input.layout; + return context.getLayoutState( lookup.view ) != lookup.input; } ); return it == attaches.end(); } @@ -155,12 +162,14 @@ namespace crg { using rpHolder::operator==; - auto & renderPass = m_passes[passIndex].renderPass; + auto & data = m_passes[passIndex]; + auto previousState = context.getPrevPipelineState(); + auto nextState = context.getNextPipelineState(); - if ( renderPass + if ( data.renderPass && rpHolder::checkAttaches( context, m_passes[passIndex].attaches ) - && m_srcState == context.getPrevPipelineState() - && m_dstState == context.getNextPipelineState() ) + && data.previousState == previousState + && data.nextState == nextState ) { return false; } @@ -168,8 +177,8 @@ namespace crg m_passes[passIndex].cleanup( m_context ); doCreateRenderPass( context , runnable - , context.getPrevPipelineState() - , context.getNextPipelineState() + , previousState + , nextState , passIndex ); doInitialiseRenderArea( passIndex ); return true; @@ -285,8 +294,8 @@ namespace crg , depthReference.layout ? &depthReference : nullptr , 0u , nullptr }; - m_srcState = previousState; - m_dstState = nextState; + data.previousState = previousState; + data.nextState = nextState; VkSubpassDependencyArray dependencies{ { VK_SUBPASS_EXTERNAL , 0u diff --git a/source/RenderGraph/RunnablePasses/RenderQuadHolder.cpp b/source/RenderGraph/RunnablePasses/RenderQuadHolder.cpp index 0d8700e..4be8dbf 100644 --- a/source/RenderGraph/RunnablePasses/RenderQuadHolder.cpp +++ b/source/RenderGraph/RunnablePasses/RenderQuadHolder.cpp @@ -77,6 +77,11 @@ namespace crg , m_config.texcoordConfig ); doPreparePipelineStates( renderSize, renderPass, std::move( blendState ) ); } + else if ( m_renderPass != renderPass ) + { + m_pipeline.resetPipeline( m_pipeline.getProgram( index ), index ); + doPreparePipelineStates( renderSize, renderPass, std::move( blendState ) ); + } if ( m_renderPass && m_renderPass != renderPass ) {