2
0
Panagiotis Christopoulos Charitos 8 жил өмнө
parent
commit
3c2f1ac9b5

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

@@ -45,7 +45,7 @@ public:
 	const RenderGraphDescription* m_descr = nullptr;
 	const RenderGraphDescription* m_descr = nullptr;
 	DynamicArrayAuto<Batch> m_batches;
 	DynamicArrayAuto<Batch> m_batches;
 
 
-	BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS> m_satisfiedConsumerRts = {false}; ///< XXX
+	BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS> m_satisfiedRts = {false}; ///< XXX
 	BitSet<MAX_RENDER_GRAPH_BUFFERS> m_satisfiedConsumerBuffers = {false}; ///< XXX
 	BitSet<MAX_RENDER_GRAPH_BUFFERS> m_satisfiedConsumerBuffers = {false}; ///< XXX
 
 
 	BakeContext(const StackAllocator<U8>& alloc)
 	BakeContext(const StackAllocator<U8>& alloc)
@@ -94,25 +94,27 @@ Error RenderGraph::dumpDependencyDotFile(const BakeContext& ctx, CString path) c
 
 
 	for(U batchIdx = 0; batchIdx < ctx.m_batches.getSize(); ++batchIdx)
 	for(U batchIdx = 0; batchIdx < ctx.m_batches.getSize(); ++batchIdx)
 	{
 	{
-		ANKI_CHECK(file.writeText("\tsubgraph cluster_%u {\n", batchIdx));
-		ANKI_CHECK(file.writeText("\t\tlabel=\"batch_%u\";\n", batchIdx));
+		ANKI_CHECK(file.writeText("\t{rank=\"same\";"));
+		for(U32 passIdx : ctx.m_batches[batchIdx].m_passes)
+		{
+			ANKI_CHECK(file.writeText("\"%s\";", &ctx.m_descr->m_passes[passIdx]->m_name[0]));
+		}
+		ANKI_CHECK(file.writeText("}\n"));
 
 
 		for(U32 passIdx : ctx.m_batches[batchIdx].m_passes)
 		for(U32 passIdx : ctx.m_batches[batchIdx].m_passes)
 		{
 		{
 			for(U32 depIdx : ctx.m_passes[passIdx].m_dependsOn)
 			for(U32 depIdx : ctx.m_passes[passIdx].m_dependsOn)
 			{
 			{
-				ANKI_CHECK(file.writeText("\t\t\"%s\"->\"%s\";\n",
+				ANKI_CHECK(file.writeText("\t\"%s\"->\"%s\";\n",
 					&ctx.m_descr->m_passes[depIdx]->m_name[0],
 					&ctx.m_descr->m_passes[depIdx]->m_name[0],
 					&ctx.m_descr->m_passes[passIdx]->m_name[0]));
 					&ctx.m_descr->m_passes[passIdx]->m_name[0]));
 			}
 			}
 
 
 			if(ctx.m_passes[passIdx].m_dependsOn.getSize() == 0)
 			if(ctx.m_passes[passIdx].m_dependsOn.getSize() == 0)
 			{
 			{
-				ANKI_CHECK(file.writeText("\t\tNONE->\"%s\";\n", &ctx.m_descr->m_passes[passIdx]->m_name[0]));
+				ANKI_CHECK(file.writeText("\tNONE->\"%s\";\n", &ctx.m_descr->m_passes[passIdx]->m_name[0]));
 			}
 			}
 		}
 		}
-
-		ANKI_CHECK(file.writeText("\t}\n"));
 	}
 	}
 
 
 	ANKI_CHECK(file.writeText("}"));
 	ANKI_CHECK(file.writeText("}"));
@@ -167,22 +169,29 @@ Bool RenderGraph::passADependsOnB(BakeContext& ctx, const RenderPassBase& a, con
 {
 {
 	Bool depends = false;
 	Bool depends = false;
 
 
-	BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS> rtIntersection =
-		(a.m_consumerRtMask & ~ctx.m_satisfiedConsumerRts) & b.m_producerRtMask;
-	if(rtIntersection.getAny())
+	/// Compute the 3 types of dependencies
+	BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS> aReadBWrite = a.m_consumerRtMask & b.m_producerRtMask;
+	BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS> aWriteBRead = a.m_producerRtMask & b.m_consumerRtMask;
+	BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS> aWriteBWrite = a.m_producerRtMask & b.m_producerRtMask;
+
+	BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS> fullDep = aReadBWrite | aWriteBRead | aWriteBWrite;
+	fullDep &= ~ctx.m_satisfiedRts;
+
+	if(fullDep.getAny())
 	{
 	{
 		// There is overlap
 		// There is overlap
 		for(const RenderPassDependency& dep : a.m_consumers)
 		for(const RenderPassDependency& dep : a.m_consumers)
 		{
 		{
-			if(dep.m_isTexture && rtIntersection.get(dep.m_renderTargetHandle))
+			if(dep.m_isTexture && fullDep.get(dep.m_renderTargetHandle))
 			{
 			{
 				depends = true;
 				depends = true;
-				ANKI_ASSERT(!ctx.m_satisfiedConsumerRts.get(dep.m_renderTargetHandle));
-				ctx.m_satisfiedConsumerRts.set(dep.m_renderTargetHandle);
+				ANKI_ASSERT(!ctx.m_satisfiedRts.get(dep.m_renderTargetHandle));
+				ctx.m_satisfiedRts.set(dep.m_renderTargetHandle);
 			}
 			}
 		}
 		}
 	}
 	}
 
 
+#if 0
 	BitSet<MAX_RENDER_GRAPH_BUFFERS> bufferIntersection =
 	BitSet<MAX_RENDER_GRAPH_BUFFERS> bufferIntersection =
 		(a.m_consumerBufferMask & ~ctx.m_satisfiedConsumerBuffers) & b.m_producerBufferMask;
 		(a.m_consumerBufferMask & ~ctx.m_satisfiedConsumerBuffers) & b.m_producerBufferMask;
 	if(bufferIntersection.getAny())
 	if(bufferIntersection.getAny())
@@ -198,6 +207,7 @@ Bool RenderGraph::passADependsOnB(BakeContext& ctx, const RenderPassBase& a, con
 			}
 			}
 		}
 		}
 	}
 	}
+#endif
 
 
 	return depends;
 	return depends;
 }
 }
@@ -242,12 +252,14 @@ void RenderGraph::compileNewGraph(const RenderGraphDescription& descr)
 	// Find the dependencies between passes
 	// Find the dependencies between passes
 	for(U i = 0; i < passCount; ++i)
 	for(U i = 0; i < passCount; ++i)
 	{
 	{
-		ctx.m_satisfiedConsumerRts.unsetAll();
+		ctx.m_satisfiedRts.unsetAll();
 		ctx.m_satisfiedConsumerBuffers.unsetAll();
 		ctx.m_satisfiedConsumerBuffers.unsetAll();
 
 
 		ctx.m_passes.emplaceBack(m_tmpAlloc);
 		ctx.m_passes.emplaceBack(m_tmpAlloc);
 		const RenderPassBase& crntPass = *descr.m_passes[i];
 		const RenderPassBase& crntPass = *descr.m_passes[i];
-		for(U j = 0; j < i; ++j)
+
+		U j = i;
+		while(j--)
 		{
 		{
 			const RenderPassBase& prevPass = *descr.m_passes[j];
 			const RenderPassBase& prevPass = *descr.m_passes[j];
 			if(passADependsOnB(ctx, crntPass, prevPass))
 			if(passADependsOnB(ctx, crntPass, prevPass))

+ 122 - 0
src/anki/renderer/ShadowMapping.cpp

@@ -649,4 +649,126 @@ void ShadowMapping::setPostRunBarriers(RenderingContext& ctx)
 	cmdb->informTextureCurrentUsage(m_omniTexArray, TextureUsageBit::SAMPLED_FRAGMENT);
 	cmdb->informTextureCurrentUsage(m_omniTexArray, TextureUsageBit::SAMPLED_FRAGMENT);
 }
 }
 
 
+void ShadowMapping::prepareBuildCommandBuffers2(RenderingContext& ctx)
+{
+	ANKI_TRACE_SCOPED_EVENT(RENDER_SM);
+
+	for(PointLightQueueElement* light : ctx.m_renderQueue->m_shadowPointLights)
+	{
+		for(U face = 0; face < 6; ++face)
+		{
+			U32 tileIdx;
+			ANKI_ASSERT(light->m_shadowRenderQueues[face]);
+			const U64 faceTimestamp = light->m_shadowRenderQueues[face]->m_timestamp;
+			const Bool failed = allocateTile(faceTimestamp, light->m_uuid, face, tileIdx);
+
+			if(!failed)
+			{
+				Tile& tile = m_tiles[tileIdx];
+				Bool shouldRender = shouldRenderTile(faceTimestamp, light->m_uuid, face, tile);
+				if(shouldRender)
+				{
+					tile.m_face = face;
+					tile.m_lightUuid = light->m_uuid;
+					tile.m_timestamp = faceTimestamp;
+
+					// TODO: Append it to a list
+				}
+			}
+			else
+			{
+				// Allocation failed, point to a random tile
+				tileIdx = 0;
+			}
+
+			// TODO: Update the PointLightQueueElement info to be passed to the shader
+		}
+	}
+
+	for()
+}
+
+Bool ShadowMapping::shouldRenderTile(U64 lightTimestamp, U64 lightUuid, U32 face, const Tile& tileIdx)
+{
+	if(tileIdx.m_face == face && tileIdx.m_lightUuid == lightUuid && tileIdx.m_timestamp >= lightTimestamp)
+	{
+		return false;
+	}
+	return true;
+}
+
+Bool ShadowMapping::allocateTile(U64 lightTimestamp, U64 lightUuid, U32 face, U32& tileAllocated)
+{
+	ANKI_ASSERT(lightTimestamp > 0);
+	ANKI_ASSERT(lightUuid > 0);
+	ANKI_ASSERT(face <= 6);
+
+	tileAllocated = MAX_U32;
+
+	// First, try to see if the light/face is in the cache
+	TileKey key = TileKey{lightUuid, U8(face)};
+	auto it = m_lightUuidToTileIdx.find(key);
+	if(it != m_lightUuidToTileIdx.getEnd())
+	{
+		const U32 tileIdx = *it;
+		if(m_tiles[tileIdx].m_lightUuid == lightUuid && m_tiles[tileIdx].m_face == face)
+		{
+			// Found it
+			tileAllocated = tileIdx;
+			return true;
+		}
+		else
+		{
+			// Cache entry is wrong, remove it
+			m_lightUuidToTileIdx.erase(getAllocator(), it);
+		}
+	}
+
+	// 2nd and 3rd choice, find an empty tile or some tile to re-use
+	U32 emptyTile = MAX_U32;
+	U32 tileToKick = MAX_U32;
+	U64 tileToKickMinTimestamp = MAX_U64;
+	for(U32 tileIdx = 0; tileIdx < m_tiles.getSize(); ++tileIdx)
+	{
+		if(m_tiles[tileIdx].m_lightUuid == 0)
+		{
+			// Found an empty
+			emptyTile = tileIdx;
+			break;
+		}
+		else if(m_tiles[tileIdx].m_timestamp != m_r->getGlobalTimestamp()
+			&& m_tiles[tileIdx].m_timestamp < tileToKickMinTimestamp)
+		{
+			tileToKick = tileIdx;
+			tileToKickMinTimestamp = m_tiles[tileIdx].m_timestamp;
+		}
+	}
+
+	Bool failed = false;
+	if(emptyTile != MAX_U32)
+	{
+		tileAllocated = emptyTile;
+	}
+	else if(tileToKick != MAX_U32)
+	{
+		tileAllocated = tileToKick;
+	}
+	else
+	{
+		// Point to something random and hope for the best
+		failed = true;
+		tileAllocated = 0;
+		ANKI_R_LOGW("There is not enough space in the shadow atlas for more shadow maps. "
+					"Increase the r.shadowMapping.tileCountPerRowOrColumn or decrease the scene's light count");
+	}
+
+	// Update the cache
+	if(!failed)
+	{
+		m_lightUuidToTileIdx.pushBack(getAllocator(), key, tileAllocated);
+	}
+
+	return failed;
+}
+
 } // end namespace anki
 } // end namespace anki

+ 35 - 0
src/anki/renderer/ShadowMapping.h

@@ -34,6 +34,8 @@ anki_internal:
 
 
 	void prepareBuildCommandBuffers(RenderingContext& ctx);
 	void prepareBuildCommandBuffers(RenderingContext& ctx);
 
 
+	void prepareBuildCommandBuffers2(RenderingContext& ctx);
+
 	void buildCommandBuffers(RenderingContext& ctx, U threadId, U threadCount);
 	void buildCommandBuffers(RenderingContext& ctx, U threadId, U threadCount);
 
 
 	void setPreRunBarriers(RenderingContext& ctx);
 	void setPreRunBarriers(RenderingContext& ctx);
@@ -82,6 +84,39 @@ private:
 	ShaderProgramResourcePtr m_esmResolveProg;
 	ShaderProgramResourcePtr m_esmResolveProg;
 	ShaderProgramPtr m_esmResolveGrProg;
 	ShaderProgramPtr m_esmResolveGrProg;
 
 
+#if 1
+	class Tile
+	{
+	public:
+		U64 m_timestamp = 0;
+		U64 m_lightUuid = 0;
+		U8 m_face = 0;
+
+		Array<F32, 4> m_uv;
+		Array<U32, 2> m_viewportXY;
+	};
+
+	DynamicArray<Tile> m_tiles;
+	U32 m_tileCountPerRowOrColumn = 0;
+
+	class TileKey
+	{
+	public:
+		U64 m_lightUuid;
+		U8 m_face;
+
+		U64 computeHash() const
+		{
+			return anki::computeHash(this, sizeof(*this), 693);
+		}
+	};
+
+	HashMap<TileKey, U32> m_lightUuidToTileIdx;
+
+	Bool allocateTile(U64 lightTimestamp, U64 lightUuid, U32 face, U32& tileAllocated);
+	static Bool shouldRenderTile(U64 lightTimestamp, U64 lightUuid, U32 face, const Tile& tileIdx);
+#endif
+
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& initializer);
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& initializer);
 
 
 	/// Find the best shadowmap for that light
 	/// Find the best shadowmap for that light

+ 35 - 2
tests/gr/Gr.cpp

@@ -1600,11 +1600,44 @@ ANKI_TEST(Gr, RenderGraph)
 		pass.newConsumer({giGbuffDepthRt, TextureUsageBit::SAMPLED_FRAGMENT});
 		pass.newConsumer({giGbuffDepthRt, TextureUsageBit::SAMPLED_FRAGMENT});
 		pass.newConsumer({giGbuffDiffRt, TextureUsageBit::SAMPLED_FRAGMENT});
 		pass.newConsumer({giGbuffDiffRt, TextureUsageBit::SAMPLED_FRAGMENT});
 
 
-		pass.newConsumer({giGiLightRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass.newProducer({giGiLightRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
 	}
 	}
 
 
-	rgraph->compileNewGraph(descr);
+	// Gbuffer
+	RenderTargetHandle gbuffRt0 = descr.newRenderTarget("Gbuff rt0", texInf);
+	RenderTargetHandle gbuffRt1 = descr.newRenderTarget("Gbuff rt1", texInf);
+	RenderTargetHandle gbuffRt2 = descr.newRenderTarget("Gbuff rt2", texInf);
+	RenderTargetHandle gbuffDepth = descr.newRenderTarget("Gbuff rt2", texInf);
+	{
+		GraphicsRenderPassInfo& pass = descr.newGraphicsRenderPass("G-buffer");
+		pass.newConsumer({gbuffRt0, TextureUsageBit::NONE});
+		pass.newConsumer({gbuffRt1, TextureUsageBit::NONE});
+		pass.newConsumer({gbuffRt2, TextureUsageBit::NONE});
+		pass.newConsumer({gbuffDepth, TextureUsageBit::NONE});
+
+		pass.newProducer({gbuffRt0, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass.newProducer({gbuffRt1, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass.newProducer({gbuffRt2, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass.newProducer({gbuffDepth, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+	}
+
+	// Light
+	RenderTargetHandle lightRt = descr.newRenderTarget("Light", texInf);
+	{
+		GraphicsRenderPassInfo& pass = descr.newGraphicsRenderPass("Light shading");
 
 
+		pass.newConsumer({lightRt, TextureUsageBit::NONE});
+		pass.newConsumer({gbuffRt0, TextureUsageBit::SAMPLED_FRAGMENT});
+		pass.newConsumer({gbuffRt1, TextureUsageBit::SAMPLED_FRAGMENT});
+		pass.newConsumer({gbuffRt2, TextureUsageBit::SAMPLED_FRAGMENT});
+		pass.newConsumer({gbuffDepth, TextureUsageBit::SAMPLED_FRAGMENT});
+		pass.newConsumer({smExpRt, TextureUsageBit::SAMPLED_FRAGMENT});
+		pass.newConsumer({giGiLightRt, TextureUsageBit::SAMPLED_FRAGMENT});
+
+		pass.newProducer({giGiLightRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+	}
+
+	rgraph->compileNewGraph(descr);
 	COMMON_END()
 	COMMON_END()
 }
 }