Forráskód Böngészése

[OPTIMIZATION] Optimize command buffer generation on Vulkan

Panagiotis Christopoulos Charitos 8 éve
szülő
commit
62b3e2f1a1

+ 1 - 0
src/anki/core/App.cpp

@@ -418,6 +418,7 @@ Error App::mainLoop()
 		timer.stop();
 		if(timer.getElapsedTime() < m_timerTick)
 		{
+			ANKI_TRACE_SCOPED_EVENT(TIMER_TICK_SLEEP);
 			HighRezTimer::sleep(m_timerTick - timer.getElapsedTime());
 		}
 

+ 3 - 1
src/anki/core/Trace.cpp

@@ -36,6 +36,7 @@ static Array<const char*, U(TraceEventType::COUNT)> eventNames = {{"RESOURCE_ALL
 	"RENDERER_COMMAND_BUFFER_BUILDING",
 	"RENDERER_LIGHT_BINNING",
 	"GR_RENDER_GRAPH",
+	"GR_COMMAND_BUFFER_RESET",
 	"GL_THREAD",
 	"GL_2ND_LEVEL_CMD_BUFFER",
 	"GL_BIND_RESOURCES",
@@ -48,7 +49,8 @@ static Array<const char*, U(TraceEventType::COUNT)> eventNames = {{"RESOURCE_ALL
 	"VK_DESCRIPTOR_SET_GET_OR_CREATE",
 	"SWAP_BUFFERS",
 	"BARRIER_WAIT",
-	"LUA_EXEC"}};
+	"LUA_EXEC",
+	"TIMER_TICK_SLEEP"}};
 
 static Array<const char*, U(TraceCounterType::COUNT)> counterNames = {{"GR_DRAWCALLS",
 	"GR_VERTICES",

+ 2 - 0
src/anki/core/Trace.h

@@ -47,6 +47,7 @@ enum class TraceEventType
 	RENDERER_COMMAND_BUFFER_BUILDING,
 	RENDERER_LIGHT_BINNING,
 	GR_RENDER_GRAPH,
+	GR_COMMAND_BUFFER_RESET,
 	GL_THREAD,
 	GL_2ND_LEVEL_CMD_BUFFER,
 	GL_BIND_RESOURCES,
@@ -60,6 +61,7 @@ enum class TraceEventType
 	SWAP_BUFFERS,
 	BARRIER_WAIT,
 	LUA_EXEC,
+	TIMER_TICK_SLEEP,
 
 	COUNT
 };

+ 27 - 15
src/anki/gr/RenderGraph.cpp

@@ -86,13 +86,16 @@ public:
 class RenderGraph::Pass
 {
 public:
+	// WARNING!!!!!: Whatever you put here needs manual destruction in RenderGraph::reset()
+
 	DynamicArray<U32> m_dependsOn;
 
 	RenderPassWorkCallback m_callback;
 	void* m_userData;
 
 	DynamicArray<CommandBufferPtr> m_secondLevelCmdbs;
-	FramebufferPtr m_fb;
+	/// Will reuse the m_secondLevelCmdbInitInfo.m_framebuffer to get the framebuffer.
+	CommandBufferInitInfo m_secondLevelCmdbInitInfo;
 	Array<U32, 4> m_fbRenderArea;
 	Array<TextureUsageBit, MAX_COLOR_ATTACHMENTS> m_colorUsages = {}; ///< For beginRender pass
 	TextureUsageBit m_dsUsage = TextureUsageBit::NONE; ///< For beginRender pass
@@ -108,6 +111,16 @@ public:
 		DepthStencilAspectBit m_aspect;
 	};
 	DynamicArray<ConsumedTextureInfo> m_consumedTextures;
+
+	FramebufferPtr& fb()
+	{
+		return m_secondLevelCmdbInitInfo.m_framebuffer;
+	}
+
+	const FramebufferPtr& fb() const
+	{
+		return m_secondLevelCmdbInitInfo.m_framebuffer;
+	}
 };
 
 /// A batch of render passes. These passes can run in parallel.
@@ -292,7 +305,7 @@ void RenderGraph::reset()
 
 	for(Pass& p : m_ctx->m_passes)
 	{
-		p.m_fb.reset(nullptr);
+		p.fb().reset(nullptr);
 		p.m_secondLevelCmdbs.destroy(m_ctx->m_alloc);
 	}
 
@@ -587,7 +600,7 @@ void RenderGraph::initRenderPassesAndSetDeps(const RenderGraphDescription& descr
 
 			if(graphicsPass.hasFramebuffer())
 			{
-				outPass.m_fb = getOrCreateFramebuffer(
+				outPass.fb() = getOrCreateFramebuffer(
 					graphicsPass.m_fbInitInfo, &graphicsPass.m_rtHandles[0], graphicsPass.m_fbHash);
 
 				outPass.m_fbRenderArea = graphicsPass.m_fbRenderArea;
@@ -623,19 +636,15 @@ void RenderGraph::initRenderPassesAndSetDeps(const RenderGraphDescription& descr
 					}
 				}
 
-				// Create the second level command buffers
+				// Do some pre-work for the second level command buffers
 				if(inPass.m_secondLevelCmdbsCount)
 				{
 					outPass.m_secondLevelCmdbs.create(alloc, inPass.m_secondLevelCmdbsCount);
-					CommandBufferInitInfo cmdbInit;
+					CommandBufferInitInfo& cmdbInit = outPass.m_secondLevelCmdbInitInfo;
 					cmdbInit.m_flags = CommandBufferFlag::GRAPHICS_WORK | CommandBufferFlag::SECOND_LEVEL;
-					cmdbInit.m_framebuffer = outPass.m_fb;
+					ANKI_ASSERT(cmdbInit.m_framebuffer.isCreated());
 					cmdbInit.m_colorAttachmentUsages = outPass.m_colorUsages;
 					cmdbInit.m_depthStencilAttachmentUsage = outPass.m_dsUsage;
-					for(U cmdbIdx = 0; cmdbIdx < inPass.m_secondLevelCmdbsCount; ++cmdbIdx)
-					{
-						outPass.m_secondLevelCmdbs[cmdbIdx] = getManager().newCommandBuffer(cmdbInit);
-					}
 				}
 			}
 			else
@@ -914,7 +923,7 @@ BufferPtr RenderGraph::getBuffer(RenderPassBufferHandle handle) const
 	return m_ctx->m_buffers[handle.m_idx].m_buffer;
 }
 
-void RenderGraph::runSecondLevel(U32 threadIdx) const
+void RenderGraph::runSecondLevel(U32 threadIdx)
 {
 	ANKI_TRACE_SCOPED_EVENT(GR_RENDER_GRAPH);
 	ANKI_ASSERT(m_ctx);
@@ -923,11 +932,14 @@ void RenderGraph::runSecondLevel(U32 threadIdx) const
 	ctx.m_rgraph = this;
 	ctx.m_currentSecondLevelCommandBufferIndex = threadIdx;
 
-	for(const Pass& p : m_ctx->m_passes)
+	for(Pass& p : m_ctx->m_passes)
 	{
 		const U size = p.m_secondLevelCmdbs.getSize();
 		if(threadIdx < size)
 		{
+			ANKI_ASSERT(!p.m_secondLevelCmdbs[threadIdx].isCreated());
+			p.m_secondLevelCmdbs[threadIdx] = getManager().newCommandBuffer(p.m_secondLevelCmdbInitInfo);
+
 			ctx.m_commandBuffer = p.m_secondLevelCmdbs[threadIdx];
 			ctx.m_secondLevelCommandBufferCount = size;
 			ctx.m_passIdx = &p - &m_ctx->m_passes[0];
@@ -980,9 +992,9 @@ void RenderGraph::run() const
 		{
 			const Pass& pass = m_ctx->m_passes[passIdx];
 
-			if(pass.m_fb.isCreated())
+			if(pass.fb().isCreated())
 			{
-				cmdb->beginRenderPass(pass.m_fb,
+				cmdb->beginRenderPass(pass.fb(),
 					pass.m_colorUsages,
 					pass.m_dsUsage,
 					pass.m_fbRenderArea[0],
@@ -1007,7 +1019,7 @@ void RenderGraph::run() const
 				}
 			}
 
-			if(pass.m_fb.isCreated())
+			if(pass.fb().isCreated())
 			{
 				cmdb->endRenderPass();
 			}

+ 1 - 1
src/anki/gr/RenderGraph.h

@@ -609,7 +609,7 @@ public:
 	/// @{
 
 	/// Will call a number of RenderPassWorkCallback that populate 2nd level command buffers.
-	void runSecondLevel(U32 threadIdx) const;
+	void runSecondLevel(U32 threadIdx);
 	/// @}
 
 	/// @name 3rd step methods

+ 14 - 8
src/anki/gr/Texture.h

@@ -35,11 +35,14 @@ public:
 
 	U64 computeHash() const
 	{
-		return anki::computeHash(this, sizeof(*this));
+		const U8* const first = reinterpret_cast<const U8* const>(&m_minLod);
+		const U8* const last = reinterpret_cast<const U8* const>(&m_repeat) + sizeof(m_repeat);
+		const U size = last - first;
+		ANKI_ASSERT(size
+			== sizeof(F32) * 2 + sizeof(SamplingFilter) * 2 + sizeof(CompareOperation) + sizeof(I8) + sizeof(Bool8));
+		return anki::computeHash(first, size);
 	}
 };
-static_assert(
-	sizeof(SamplerInitInfo) == sizeof(GrBaseInitInfo) + 16, "Class needs to be tightly packed since we hash it");
 
 /// Texture initializer.
 class alignas(4) TextureInitInfo : public GrBaseInitInfo
@@ -51,8 +54,7 @@ public:
 	U32 m_layerCount = 1; ///< Relevant only for texture arrays.
 
 	TextureUsageBit m_usage = TextureUsageBit::NONE; ///< How the texture will be used.
-	TextureUsageBit m_initialUsage = TextureUsageBit::NONE; ///< It's initial usage.
-
+	TextureUsageBit m_initialUsage = TextureUsageBit::NONE; ///< Its initial usage.
 	TextureType m_type = TextureType::_2D;
 
 	U8 m_mipmapsCount = 1;
@@ -74,11 +76,15 @@ public:
 
 	U64 computeHash() const
 	{
-		return anki::computeHash(this, sizeof(*this));
+		const U8* const first = reinterpret_cast<const U8* const>(&m_width);
+		const U8* const last = reinterpret_cast<const U8* const>(&m_samples) + sizeof(m_samples);
+		const U size = last - first;
+		ANKI_ASSERT(size
+			== sizeof(U32) * 4 + sizeof(TextureUsageBit) * 2 + sizeof(TextureType) + sizeof(U8) + sizeof(PixelFormat)
+				+ sizeof(U8));
+		return appendHash(first, size, m_sampling.computeHash());
 	}
 };
-static_assert(sizeof(TextureInitInfo) == sizeof(GrBaseInitInfo) + 28 + sizeof(SamplerInitInfo),
-	"Class needs to be tightly packed since we hash it");
 
 /// GPU texture.
 class Texture : public GrObject

+ 7 - 10
src/anki/gr/vulkan/CommandBufferFactory.cpp

@@ -22,6 +22,8 @@ void MicroCommandBuffer::destroy()
 
 void MicroCommandBuffer::reset()
 {
+	ANKI_TRACE_SCOPED_EVENT(GR_COMMAND_BUFFER_RESET);
+
 	ANKI_ASSERT(m_refcount.load() == 0);
 	ANKI_ASSERT(!m_fence.isCreated() || m_fence->done());
 
@@ -88,8 +90,8 @@ Error CommandBufferThreadAllocator::newCommandBuffer(CommandBufferFlag cmdbFlags
 {
 	cmdbFlags = cmdbFlags & (CommandBufferFlag::SECOND_LEVEL | CommandBufferFlag::SMALL_BATCH);
 
-	Bool secondLevel = !!(cmdbFlags & CommandBufferFlag::SECOND_LEVEL);
-	Bool smallBatch = !!(cmdbFlags & CommandBufferFlag::SMALL_BATCH);
+	const Bool secondLevel = !!(cmdbFlags & CommandBufferFlag::SECOND_LEVEL);
+	const Bool smallBatch = !!(cmdbFlags & CommandBufferFlag::SMALL_BATCH);
 	CmdbType& type = m_types[secondLevel][smallBatch];
 
 	// Move the deleted to (possibly) in-use
@@ -199,14 +201,8 @@ void CommandBufferThreadAllocator::deleteCommandBuffer(MicroCommandBuffer* ptr)
 {
 	ANKI_ASSERT(ptr);
 
-	Bool secondLevel = !!(ptr->m_flags & CommandBufferFlag::SECOND_LEVEL);
-	Bool smallBatch = !!(ptr->m_flags & CommandBufferFlag::SMALL_BATCH);
-
-	if(secondLevel)
-	{
-		// We can safely reset the 2nd level cmdbs early
-		ptr->reset();
-	}
+	const Bool secondLevel = !!(ptr->m_flags & CommandBufferFlag::SECOND_LEVEL);
+	const Bool smallBatch = !!(ptr->m_flags & CommandBufferFlag::SMALL_BATCH);
 
 	CmdbType& type = m_types[secondLevel][smallBatch];
 
@@ -248,6 +244,7 @@ Error CommandBufferFactory::newCommandBuffer(ThreadId tid, CommandBufferFlag cmd
 {
 	CommandBufferThreadAllocator* alloc = nullptr;
 
+	// Get the thread allocator
 	{
 		LockGuard<SpinLock> lock(m_threadAllocMtx);
 

+ 9 - 1
src/anki/gr/vulkan/CommandBufferImpl.cpp

@@ -36,13 +36,21 @@ CommandBufferImpl::~CommandBufferImpl()
 
 Error CommandBufferImpl::init(const CommandBufferInitInfo& init)
 {
-	// Store some of the init info for later
+	m_tid = Thread::getCurrentThreadId();
 	m_flags = init.m_flags;
+
+	ANKI_CHECK(getGrManagerImpl().getCommandBufferFactory().newCommandBuffer(m_tid, m_flags, m_microCmdb));
+	m_handle = m_microCmdb->getHandle();
+
+	m_alloc = m_microCmdb->getFastAllocator();
+
+	// Store some of the init info for later
 	if(!!(m_flags & CommandBufferFlag::SECOND_LEVEL))
 	{
 		m_activeFb = init.m_framebuffer;
 		m_colorAttachmentUsages = init.m_colorAttachmentUsages;
 		m_depthStencilAttachmentUsage = init.m_depthStencilAttachmentUsage;
+		m_state.beginRenderPass(m_activeFb);
 	}
 
 	return Error::NONE;

+ 0 - 3
src/anki/gr/vulkan/CommandBufferImpl.h

@@ -352,7 +352,6 @@ private:
 	Bool8 m_finalized = false;
 	Bool8 m_empty = true;
 	Bool8 m_beganRecording = false;
-	Bool8 m_initialized = false;
 	ThreadId m_tid = ~ThreadId(0);
 #if ANKI_EXTRA_CHECKS
 	U32 m_commandCount = 0;
@@ -434,8 +433,6 @@ private:
 	U16 m_secondLevelAtomCount = 0;
 	/// @}
 
-	void lazyInit();
-
 	/// Some common operations per command.
 	void commandCommon();
 

+ 0 - 26
src/anki/gr/vulkan/CommandBufferImpl.inl.h

@@ -560,8 +560,6 @@ inline void CommandBufferImpl::commandCommon()
 {
 	ANKI_ASSERT(!m_finalized);
 
-	lazyInit();
-
 #if ANKI_EXTRA_CHECKS
 	++m_commandCount;
 #endif
@@ -580,30 +578,6 @@ inline void CommandBufferImpl::commandCommon()
 	ANKI_ASSERT(m_handle);
 }
 
-inline void CommandBufferImpl::lazyInit()
-{
-	if(m_initialized)
-	{
-		return;
-	}
-	m_initialized = true;
-
-	m_tid = Thread::getCurrentThreadId();
-
-	if(getGrManagerImpl().getCommandBufferFactory().newCommandBuffer(m_tid, m_flags, m_microCmdb))
-	{
-		ANKI_VK_LOGF("Cannot recover");
-	}
-	m_handle = m_microCmdb->getHandle();
-
-	m_alloc = m_microCmdb->getFastAllocator();
-
-	if(!!(m_flags & CommandBufferFlag::SECOND_LEVEL))
-	{
-		m_state.beginRenderPass(m_activeFb);
-	}
-}
-
 inline void CommandBufferImpl::flushBatches(CommandBufferCommandType type)
 {
 	if(type != m_lastCmdType)

+ 1 - 1
src/anki/gr/vulkan/GrManager.cpp

@@ -36,7 +36,7 @@ Error GrManager::newInstance(GrManagerInitInfo& init, GrManager*& gr)
 	GrManagerImpl* impl = alloc.newInstance<GrManagerImpl>();
 
 	// Init
-	impl->m_alloc = HeapAllocator<U8>(init.m_allocCallback, init.m_allocCallbackUserData);
+	impl->m_alloc = alloc;
 	impl->m_cacheDir.create(alloc, init.m_cacheDirectory);
 	Error err = impl->init(init);
 

+ 0 - 1
src/anki/gr/vulkan/SamplerFactory.cpp

@@ -95,7 +95,6 @@ void SamplerFactory::destroy()
 	for(auto it : m_map)
 	{
 		MicroSampler* const sampler = it;
-
 		ANKI_ASSERT(sampler->getRefcount().load() == 0 && "Someone still holds a reference to a sampler");
 		alloc.deleteInstance(sampler);
 	}

+ 1 - 7
src/anki/gr/vulkan/SamplerImpl.cpp

@@ -10,14 +10,8 @@
 namespace anki
 {
 
-Error SamplerImpl::init(const SamplerInitInfo& inf_)
+Error SamplerImpl::init(const SamplerInitInfo& inf)
 {
-	SamplerInitInfo inf = inf_;
-
-	// Set a constant name because the name will take part in hashing. If it's unique every time then there is no point
-	// in having the SamplerFactory
-	inf.setName("SamplerSampler");
-
 	return getGrManagerImpl().getSamplerFactory().newInstance(inf, m_sampler);
 }
 

+ 1 - 4
src/anki/gr/vulkan/TextureImpl.cpp

@@ -45,10 +45,7 @@ TextureImpl::~TextureImpl()
 Error TextureImpl::init(const TextureInitInfo& init_)
 {
 	TextureInitInfo init = init_;
-
-	// Set a constant name because the name will take part in hashing. If it's unique every time then there is no point
-	// in having the SamplerFactory
-	init.m_sampling.setName("SamplForTex");
+	init.m_sampling.setName(init.getName());
 
 	ANKI_ASSERT(textureInitInfoValid(init));