Browse Source

RenderGraph: More work

Panagiotis Christopoulos Charitos 8 years ago
parent
commit
0dd60472f3

+ 29 - 0
shaders/Functions.glsl

@@ -185,6 +185,35 @@ vec3 getCubemapDirection(vec2 norm, uint faceIdx)
 	return normalize(norm.x * xDir + norm.y * yDir + zDir);
 	return normalize(norm.x * xDir + norm.y * yDir + zDir);
 }
 }
 
 
+// Convert 3D cubemap coordinates to 2D plus face index. v needs to be normalized.
+vec2 convertCubeUvs(vec3 v, out float faceIndex)
+{
+	vec3 absV = abs(v);
+	float mag;
+	vec3 uv;
+
+	if(all(greaterThanEqual(absV.zz, absV.xy)))
+	{
+		faceIndex = (v.z < 0.0) ? 5.0 : 4.0;
+		uv = vec2((v.z < 0.0) ? -v.x : v.x, -v.y);
+		mag = absV.z;
+	}
+	else if(absV.y >= absV.x)
+	{
+		faceIndex = (v.y < 0.0) ? 3.0 : 2.0;
+		uv = vec2(v.x, (v.y < 0.0) ? -v.z : v.z);
+		mag = absV.y;
+	}
+	else
+	{
+		faceIndex = (v.x < 0.0) ? 1.0 : 0.0;
+		uv = vec2((v.x < 0.0) ? v.z : -v.z, -v.y);
+		mag = absV.x;
+	}
+
+	return uv * mag * 2.0 + 0.5;
+}
+
 vec3 grayScale(vec3 col)
 vec3 grayScale(vec3 col)
 {
 {
 	float grey = (col.r + col.g + col.b) * (1.0 / 3.0);
 	float grey = (col.r + col.g + col.b) * (1.0 / 3.0);

+ 3 - 4
src/anki/gr/Common.h

@@ -190,15 +190,14 @@ public:
 	/// @name The name of the object.
 	/// @name The name of the object.
 	GrBaseInitInfo(CString name)
 	GrBaseInitInfo(CString name)
 	{
 	{
+		// Zero it because the derived class may be hashed.
+		memset(&m_name[0], 0, sizeof(m_name));
+
 		if(name)
 		if(name)
 		{
 		{
 			ANKI_ASSERT(name.getLength() <= MAX_GR_OBJECT_NAME_LENGTH);
 			ANKI_ASSERT(name.getLength() <= MAX_GR_OBJECT_NAME_LENGTH);
 			memcpy(&m_name[0], &name[0], name.getLength() + 1);
 			memcpy(&m_name[0], &name[0], name.getLength() + 1);
 		}
 		}
-		else
-		{
-			m_name[0] = '\0';
-		}
 	}
 	}
 
 
 	GrBaseInitInfo()
 	GrBaseInitInfo()

+ 123 - 182
src/anki/gr/RenderGraph.cpp

@@ -13,97 +13,76 @@
 namespace anki
 namespace anki
 {
 {
 
 
-/// Render pass or compute job.
+/// Contains some extra things the RenderPassBase cannot hold.
 class RenderGraph::Pass
 class RenderGraph::Pass
 {
 {
 public:
 public:
-	FramebufferPtr m_framebuffer;
+	DynamicArrayAuto<U32> m_dependsOn;
 
 
-	Storage<RenderGraphDependency> m_consumers;
-	Storage<RenderGraphDependency> m_producers;
-
-	Storage<Pass*> m_dependsOn;
-
-	U32 m_index;
-	Array<char, MAX_GR_OBJECT_NAME_LENGTH + 1> m_name;
+	Pass(const StackAllocator<U8>& alloc)
+		: m_dependsOn(alloc)
+	{
+	}
 };
 };
 
 
-/// Render target.
-class RenderGraph::RenderTarget
+/// A batch of render passes. These passes can run in parallel.
+class RenderGraph::Batch
 {
 {
 public:
 public:
-	TexturePtr m_tex;
-	Bool8 m_imported = false;
-	Array<char, MAX_GR_OBJECT_NAME_LENGTH + 1> m_name;
-};
+	DynamicArrayAuto<U32> m_passes;
 
 
-/// A collection of passes that can execute in parallel.
-class RenderGraph::PassBatch
-{
-public:
-	Storage<Pass*> m_passes;
+	Batch(const StackAllocator<U8>& alloc)
+		: m_passes(alloc)
+	{
+	}
 };
 };
 
 
 class RenderGraph::BakeContext
 class RenderGraph::BakeContext
 {
 {
 public:
 public:
-	Storage<PassBatch> m_batches;
+	DynamicArrayAuto<Pass> m_passes;
+	BitSet<MAX_RENDER_GRAPH_PASSES> m_passIsInBatch = {false};
+	const RenderGraphDescription* m_descr = nullptr;
+	DynamicArrayAuto<Batch> m_batches;
 
 
-	BitSet<MAX_PASSES> m_passIsInBatch = {false};
-};
+	BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS> m_satisfiedConsumerRts = {false}; ///< XXX
+	BitSet<MAX_RENDER_GRAPH_BUFFERS> m_satisfiedConsumerBuffers = {false}; ///< XXX
 
 
-template<typename T>
-void RenderGraph::Storage<T>::pushBack(StackAllocator<U8>& alloc, T&& x)
-{
-	if(m_count == m_storage)
+	BakeContext(const StackAllocator<U8>& alloc)
+		: m_passes(alloc)
+		, m_batches(alloc)
 	{
 	{
-		m_storage = max<U32>(2, m_storage * 2);
-		T* newStorage = alloc.newArray<T>(m_storage);
-		for(U i = 0; i < m_count; ++i)
-		{
-			newStorage[i] = std::move(m_arr[i]);
-		}
-
-		if(m_count)
-		{
-			alloc.deleteArray(m_arr, m_count);
-		}
-
-		m_arr = newStorage;
 	}
 	}
-
-	m_arr[m_count] = std::move(x);
-	++m_count;
-}
+};
 
 
 RenderGraph::RenderGraph(GrManager* manager, U64 hash, GrObjectCache* cache)
 RenderGraph::RenderGraph(GrManager* manager, U64 hash, GrObjectCache* cache)
 	: GrObject(manager, CLASS_TYPE, hash, cache)
 	: GrObject(manager, CLASS_TYPE, hash, cache)
 {
 {
 	ANKI_ASSERT(manager);
 	ANKI_ASSERT(manager);
 
 
-	m_tmpAlloc = StackAllocator<U8>(m_gr->getAllocator().getMemoryPool().getAllocationCallback(),
-		m_gr->getAllocator().getMemoryPool().getAllocationCallbackUserData(),
+	m_tmpAlloc = StackAllocator<U8>(getManager().getAllocator().getMemoryPool().getAllocationCallback(),
+		getManager().getAllocator().getMemoryPool().getAllocationCallbackUserData(),
 		512_B);
 		512_B);
 }
 }
 
 
 RenderGraph::~RenderGraph()
 RenderGraph::~RenderGraph()
 {
 {
-	// TODO
-}
+	reset();
 
 
-void RenderGraph::reset()
-{
-	for(RenderTarget& rt : m_renderTargets)
+	while(!m_renderTargetCache.isEmpty())
 	{
 	{
-		rt.m_tex.reset(nullptr);
-	}
-	m_renderTargets.reset();
+		auto it = m_renderTargetCache.getBegin();
+		RenderTargetCacheEntry* entry = &*it;
+		m_renderTargetCache.erase(it);
 
 
-	for(Pass& pass : m_passes)
-	{
-		pass.m_framebuffer.reset(nullptr);
+		entry->m_textures.destroy(getAllocator());
+		getAllocator().deleteInstance(entry);
 	}
 	}
-	m_passes.reset();
+}
+
+void RenderGraph::reset()
+{
+	// TODO
 }
 }
 
 
 Error RenderGraph::dumpDependencyDotFile(const BakeContext& ctx, CString path) const
 Error RenderGraph::dumpDependencyDotFile(const BakeContext& ctx, CString path) const
@@ -118,11 +97,18 @@ Error RenderGraph::dumpDependencyDotFile(const BakeContext& ctx, CString path) c
 		ANKI_CHECK(file.writeText("\tsubgraph cluster_%u {\n", 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\tlabel=\"batch_%u\";\n", batchIdx));
 
 
-		for(const Pass* pass : ctx.m_batches[batchIdx].m_passes)
+		for(U32 passIdx : ctx.m_batches[batchIdx].m_passes)
 		{
 		{
-			for(const Pass* dep : pass->m_dependsOn)
+			for(U32 depIdx : ctx.m_passes[passIdx].m_dependsOn)
+			{
+				ANKI_CHECK(file.writeText("\t\t\"%s\"->\"%s\";\n",
+					&ctx.m_descr->m_passes[depIdx]->m_name[0],
+					&ctx.m_descr->m_passes[passIdx]->m_name[0]));
+			}
+
+			if(ctx.m_passes[passIdx].m_dependsOn.getSize() == 0)
 			{
 			{
-				ANKI_CHECK(file.writeText("\t\t%s->%s;\n", &pass->m_name[0], &dep->m_name[0]));
+				ANKI_CHECK(file.writeText("\t\tNONE->\"%s\";\n", &ctx.m_descr->m_passes[passIdx]->m_name[0]));
 			}
 			}
 		}
 		}
 
 
@@ -133,49 +119,23 @@ Error RenderGraph::dumpDependencyDotFile(const BakeContext& ctx, CString path) c
 	return Error::NONE;
 	return Error::NONE;
 }
 }
 
 
-RenderGraphHandle RenderGraph::pushRenderTarget(CString name, TexturePtr tex, Bool imported)
-{
-	RenderTarget target;
-	target.m_imported = imported;
-	target.m_tex = tex;
-
-	if(name.getLength())
-	{
-		ANKI_ASSERT(name.getLength() <= MAX_GR_OBJECT_NAME_LENGTH);
-		strcpy(&target.m_name[0], &name[0]);
-	}
-	else
-	{
-		static const char* NA = "N/A";
-		strcpy(&target.m_name[0], NA);
-	}
-
-	m_renderTargets.pushBack(m_tmpAlloc, std::move(target));
-	return (m_renderTargets.getSize() - 1) & TEXTURE_TYPE;
-}
-
-RenderGraphHandle RenderGraph::importRenderTarget(CString name, TexturePtr tex)
-{
-	return pushRenderTarget(name, tex, true);
-}
-
-RenderGraphHandle RenderGraph::newRenderTarget(const TextureInitInfo& texInf)
+TexturePtr RenderGraph::getOrCreateRenderTarget(const TextureInitInfo& initInf)
 {
 {
-	auto alloc = m_gr->getAllocator();
+	auto alloc = getManager().getAllocator();
 
 
 	// Find a cache entry
 	// Find a cache entry
-	auto it = m_renderTargetCache.find(texInf);
 	RenderTargetCacheEntry* entry = nullptr;
 	RenderTargetCacheEntry* entry = nullptr;
+	auto it = m_renderTargetCache.find(initInf);
 	if(ANKI_UNLIKELY(it == m_renderTargetCache.getEnd()))
 	if(ANKI_UNLIKELY(it == m_renderTargetCache.getEnd()))
 	{
 	{
 		// Didn't found the entry, create a new one
 		// Didn't found the entry, create a new one
 
 
 		entry = alloc.newInstance<RenderTargetCacheEntry>();
 		entry = alloc.newInstance<RenderTargetCacheEntry>();
-		m_renderTargetCache.pushBack(alloc, texInf, entry);
+		m_renderTargetCache.pushBack(initInf, entry);
 	}
 	}
 	else
 	else
 	{
 	{
-		entry = *it;
+		entry = &(*it);
 	}
 	}
 	ANKI_ASSERT(entry);
 	ANKI_ASSERT(entry);
 
 
@@ -192,7 +152,7 @@ RenderGraphHandle RenderGraph::newRenderTarget(const TextureInitInfo& texInf)
 	{
 	{
 		// Create it
 		// Create it
 
 
-		tex = m_gr->newInstance<Texture>(texInf);
+		tex = getManager().newInstance<Texture>(initInf);
 
 
 		ANKI_ASSERT(entry->m_texturesInUse == 0);
 		ANKI_ASSERT(entry->m_texturesInUse == 0);
 		entry->m_textures.resize(alloc, entry->m_textures.getSize() + 1);
 		entry->m_textures.resize(alloc, entry->m_textures.getSize() + 1);
@@ -200,98 +160,59 @@ RenderGraphHandle RenderGraph::newRenderTarget(const TextureInitInfo& texInf)
 		++entry->m_texturesInUse;
 		++entry->m_texturesInUse;
 	}
 	}
 
 
-	// Create the render target
-	return pushRenderTarget(texInf.getName(), tex, false);
+	return tex;
 }
 }
 
 
-Bool RenderGraph::passADependsOnB(const Pass& a, const Pass& b)
+Bool RenderGraph::passADependsOnB(BakeContext& ctx, const RenderPassBase& a, const RenderPassBase& b)
 {
 {
-	for(const RenderGraphDependency& consumer : a.m_consumers)
+	Bool depends = false;
+
+	BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS> rtIntersection =
+		(a.m_consumerRtMask & ~ctx.m_satisfiedConsumerRts) & b.m_producerRtMask;
+	if(rtIntersection.getAny())
 	{
 	{
-		for(const RenderGraphDependency& producer : a.m_producers)
+		// There is overlap
+		for(const RenderPassDependency& dep : a.m_consumers)
 		{
 		{
-			if(consumer.m_handle == producer.m_handle)
+			if(dep.m_isTexture && rtIntersection.get(dep.m_renderTargetHandle))
 			{
 			{
-				return true;
+				depends = true;
+				ANKI_ASSERT(!ctx.m_satisfiedConsumerRts.get(dep.m_renderTargetHandle));
+				ctx.m_satisfiedConsumerRts.set(dep.m_renderTargetHandle);
 			}
 			}
 		}
 		}
 	}
 	}
 
 
-	return false;
-}
-
-RenderGraphHandle RenderGraph::registerRenderPass(CString name,
-	WeakArray<RenderTargetInfo> colorAttachments,
-	RenderTargetInfo depthStencilAttachment,
-	WeakArray<RenderGraphDependency> consumers,
-	WeakArray<RenderGraphDependency> producers,
-	RenderGraphWorkCallback callback,
-	void* userData,
-	U32 secondLevelCmdbsCount)
-{
-	Pass newPass;
-	newPass.m_index = m_passes.getSize();
-
-	// Set name
-	if(name.getLength())
-	{
-		ANKI_ASSERT(name.getLength() <= MAX_GR_OBJECT_NAME_LENGTH);
-		strcpy(&newPass.m_name[0], &name[0]);
-	}
-	else
+	BitSet<MAX_RENDER_GRAPH_BUFFERS> bufferIntersection =
+		(a.m_consumerBufferMask & ~ctx.m_satisfiedConsumerBuffers) & b.m_producerBufferMask;
+	if(bufferIntersection.getAny())
 	{
 	{
-		static const char* NA = "N/A";
-		strcpy(&newPass.m_name[0], NA);
-	}
-
-	// Set the dependencies
-	if(consumers.getSize())
-	{
-		newPass.m_consumers.m_arr = m_tmpAlloc.newArray<RenderGraphDependency>(consumers.getSize());
-		for(U i = 0; i < consumers.getSize(); ++i)
+		// There is overlap
+		for(const RenderPassDependency& dep : a.m_consumers)
 		{
 		{
-			newPass.m_consumers.m_arr[i] = consumers[i];
-		}
-
-		newPass.m_consumers.m_count = newPass.m_consumers.m_storage = consumers.getSize();
-	}
-
-	if(producers.getSize())
-	{
-		newPass.m_producers.m_arr = m_tmpAlloc.newArray<RenderGraphDependency>(producers.getSize());
-		for(U i = 0; i < producers.getSize(); ++i)
-		{
-			newPass.m_producers.m_arr[i] = producers[i];
-		}
-
-		newPass.m_producers.m_count = newPass.m_producers.m_storage = producers.getSize();
-	}
-
-	// Find the dependencies
-	for(Pass& otherPass : m_passes)
-	{
-		if(passADependsOnB(newPass, otherPass))
-		{
-			newPass.m_dependsOn.pushBack(m_tmpAlloc, &otherPass);
+			if(!dep.m_isTexture && bufferIntersection.get(dep.m_bufferHandle))
+			{
+				depends = true;
+				ANKI_ASSERT(!ctx.m_satisfiedConsumerBuffers.get(dep.m_bufferHandle));
+				ctx.m_satisfiedConsumerBuffers.set(dep.m_bufferHandle);
+			}
 		}
 		}
 	}
 	}
 
 
-	// Push pass to the passes
-	m_passes.pushBack(m_tmpAlloc, std::move(newPass));
-	return (m_passes.getSize() - 1) & RT_TYPE;
+	return depends;
 }
 }
 
 
-Bool RenderGraph::passHasUnmetDependencies(const BakeContext& ctx, const Pass& pass) const
+Bool RenderGraph::passHasUnmetDependencies(const BakeContext& ctx, U32 passIdx)
 {
 {
 	Bool depends = false;
 	Bool depends = false;
 
 
 	if(ctx.m_batches.getSize() > 0)
 	if(ctx.m_batches.getSize() > 0)
 	{
 	{
-		// Check if the passes it depends on are in a batch
+		// Check if the deps of passIdx are all in a batch
 
 
-		for(const Pass* dep : pass.m_dependsOn)
+		for(const U32 depPassIdx : ctx.m_passes[passIdx].m_dependsOn)
 		{
 		{
-			if(ctx.m_passIsInBatch.get(dep->m_index) == false)
+			if(ctx.m_passIsInBatch.get(depPassIdx) == false)
 			{
 			{
 				// Dependency pass is not in a batch
 				// Dependency pass is not in a batch
 				depends = true;
 				depends = true;
@@ -301,53 +222,73 @@ Bool RenderGraph::passHasUnmetDependencies(const BakeContext& ctx, const Pass& p
 	}
 	}
 	else
 	else
 	{
 	{
-		// First batch, check if pass depends on any pass
+		// First batch, check if passIdx depends on any pass
 
 
-		depends = pass.m_dependsOn.getSize() != 0;
+		depends = ctx.m_passes[passIdx].m_dependsOn.getSize() != 0;
 	}
 	}
 
 
 	return depends;
 	return depends;
 }
 }
 
 
-void RenderGraph::bake()
+void RenderGraph::compileNewGraph(const RenderGraphDescription& descr)
 {
 {
-	BakeContext ctx;
+	const U passCount = descr.m_passes.getSize();
+	ANKI_ASSERT(passCount > 0);
+
+	// Init the context
+	BakeContext ctx(m_tmpAlloc);
+	ctx.m_descr = &descr;
+
+	// Find the dependencies between passes
+	for(U i = 0; i < passCount; ++i)
+	{
+		ctx.m_satisfiedConsumerRts.unsetAll();
+		ctx.m_satisfiedConsumerBuffers.unsetAll();
+
+		ctx.m_passes.emplaceBack(m_tmpAlloc);
+		const RenderPassBase& crntPass = *descr.m_passes[i];
+		for(U j = 0; j < i; ++j)
+		{
+			const RenderPassBase& prevPass = *descr.m_passes[j];
+			if(passADependsOnB(ctx, crntPass, prevPass))
+			{
+				ctx.m_passes[i].m_dependsOn.emplaceBack(j);
+			}
+		}
+	}
 
 
 	// Walk the graph and create pass batches
 	// Walk the graph and create pass batches
 	U passesInBatchCount = 0;
 	U passesInBatchCount = 0;
-	while(passesInBatchCount < m_passes.getSize())
+	while(passesInBatchCount < passCount)
 	{
 	{
-		PassBatch batch;
+		Batch batch(m_tmpAlloc);
 
 
-		for(U i = 0; i < m_passes.getSize(); ++i)
+		for(U i = 0; i < passCount; ++i)
 		{
 		{
-			if(!ctx.m_passIsInBatch.get(i) && !passHasUnmetDependencies(ctx, m_passes[i]))
+			if(!ctx.m_passIsInBatch.get(i) && !passHasUnmetDependencies(ctx, i))
 			{
 			{
 				// Add to the batch
 				// Add to the batch
-				batch.m_passes.pushBack(m_tmpAlloc, &m_passes[i]);
+				++passesInBatchCount;
+				batch.m_passes.emplaceBack(i);
 			}
 			}
 		}
 		}
 
 
 		// Push back batch
 		// Push back batch
-		ctx.m_batches.pushBack(m_tmpAlloc, std::move(batch));
+		ctx.m_batches.emplaceBack(std::move(batch));
 
 
 		// Mark batch's passes done
 		// Mark batch's passes done
-		for(const Pass* pass : batch.m_passes)
+		for(U32 passIdx : ctx.m_batches.getBack().m_passes)
 		{
 		{
-			ctx.m_passIsInBatch.set(pass->m_index);
+			ctx.m_passIsInBatch.set(passIdx);
 		}
 		}
 	}
 	}
 
 
-	// Find out what barriers we need between passes
-	/*for(PassBatch& batch : ctx.m_batches)
+#if 1
+	if(dumpDependencyDotFile(ctx, "./"))
 	{
 	{
-		for(c : consumers)
-		{
-			lastProducer = findLastProducer(c);
-		}
-
-
-	}*/
+		ANKI_LOGF("Won't recover on debug code");
+	}
+#endif
 }
 }
 
 
 } // end namespace anki
 } // end namespace anki

+ 251 - 119
src/anki/gr/RenderGraph.h

@@ -7,7 +7,9 @@
 
 
 #include <anki/gr/GrObject.h>
 #include <anki/gr/GrObject.h>
 #include <anki/gr/Enums.h>
 #include <anki/gr/Enums.h>
+#include <anki/gr/Texture.h>
 #include <anki/util/HashMap.h>
 #include <anki/util/HashMap.h>
+#include <anki/util/BitSet.h>
 
 
 namespace anki
 namespace anki
 {
 {
@@ -15,30 +17,66 @@ namespace anki
 /// @addtogroup graphics
 /// @addtogroup graphics
 /// @{
 /// @{
 
 
+/// @name RenderGraph constants
+/// @{
+static constexpr U MAX_RENDER_GRAPH_PASSES = 128;
+static constexpr U MAX_RENDER_GRAPH_RENDER_TARGETS = 128;
+static constexpr U MAX_RENDER_GRAPH_BUFFERS = 64;
+/// @}
+
 /// XXX
 /// XXX
-using RenderGraphHandle = U32;
+using RenderTargetHandle = U32;
 
 
 /// XXX
 /// XXX
-using RenderGraphWorkCallback = void (*)(
+using RenderPassBufferHandle = U32;
+
+/// XXX
+using RenderPassWorkCallback = void (*)(
 	void* userData, CommandBufferPtr cmdb, U32 secondLevelCmdbIdx, U32 secondLevelCmdbCount);
 	void* userData, CommandBufferPtr cmdb, U32 secondLevelCmdbIdx, U32 secondLevelCmdbCount);
 
 
 /// XXX
 /// XXX
-class RenderGraphDependency
+class RenderPassDependency
 {
 {
+	friend class RenderGraph;
+	friend class RenderPassBase;
+
 public:
 public:
-	RenderGraphHandle m_handle; ///< Buffer or render target handle.
+	union
+	{
+		RenderTargetHandle m_renderTargetHandle;
+		RenderPassBufferHandle m_bufferHandle;
+	};
+
 	union
 	union
 	{
 	{
 		TextureUsageBit m_textureUsage;
 		TextureUsageBit m_textureUsage;
 		BufferUsageBit m_bufferUsage;
 		BufferUsageBit m_bufferUsage;
 	};
 	};
+
+	RenderPassDependency(RenderTargetHandle handle, TextureUsageBit usage)
+		: m_renderTargetHandle(handle)
+		, m_textureUsage(usage)
+		, m_isTexture(true)
+	{
+	}
+
+	RenderPassDependency(RenderPassBufferHandle handle, BufferUsageBit usage)
+		: m_bufferHandle(handle)
+		, m_bufferUsage(usage)
+		, m_isTexture(false)
+	{
+	}
+
+private:
+	Bool8 m_isTexture;
 };
 };
 
 
-/// XXX
-class RenderTargetInfo
+/// Describes how the render target will be used inside a graphics render pass.
+class RenderTargetReference
 {
 {
 public:
 public:
-	RenderGraphHandle m_renderTarget;
+	RenderTargetHandle m_renderTargetHandle;
+
 	TextureSurfaceInfo m_surface;
 	TextureSurfaceInfo m_surface;
 	AttachmentLoadOperation m_loadOperation = AttachmentLoadOperation::CLEAR;
 	AttachmentLoadOperation m_loadOperation = AttachmentLoadOperation::CLEAR;
 	AttachmentStoreOperation m_storeOperation = AttachmentStoreOperation::STORE;
 	AttachmentStoreOperation m_storeOperation = AttachmentStoreOperation::STORE;
@@ -50,6 +88,196 @@ public:
 	DepthStencilAspectBit m_aspect = DepthStencilAspectBit::NONE; ///< Relevant only for depth stencil textures.
 	DepthStencilAspectBit m_aspect = DepthStencilAspectBit::NONE; ///< Relevant only for depth stencil textures.
 };
 };
 
 
+/// The base of compute/transfer and graphics renderpasses
+class RenderPassBase
+{
+	friend class RenderGraph;
+
+public:
+	virtual ~RenderPassBase()
+	{
+		m_name.destroy(m_alloc); // To avoid the assertion
+		m_consumers.destroy(m_alloc);
+		m_producers.destroy(m_alloc);
+	}
+
+	void setWork(RenderPassWorkCallback callback, void* userData, U32 secondLeveCmdbCount)
+	{
+		m_callback = callback;
+		m_userData = userData;
+		m_secondLevelCmdbsCount = secondLeveCmdbCount;
+	}
+
+	void newConsumer(const RenderPassDependency& dep)
+	{
+		m_consumers.emplaceBack(m_alloc, dep);
+		if(dep.m_isTexture)
+		{
+			m_consumerRtMask.set(dep.m_renderTargetHandle);
+		}
+		else
+		{
+			m_consumerBufferMask.set(dep.m_bufferHandle);
+		}
+	}
+
+	void newProducer(const RenderPassDependency& dep)
+	{
+		m_producers.emplaceBack(m_alloc, dep);
+		if(dep.m_isTexture)
+		{
+			m_producerRtMask.set(dep.m_renderTargetHandle);
+		}
+		else
+		{
+			m_producerBufferMask.set(dep.m_bufferHandle);
+		}
+	}
+
+protected:
+	enum class Type : U8
+	{
+		GRAPHICS,
+		NO_GRAPHICS
+	};
+
+	Type m_type;
+
+	StackAllocator<U8> m_alloc;
+
+	RenderPassWorkCallback m_callback = nullptr;
+	void* m_userData = nullptr;
+	U32 m_secondLevelCmdbsCount = 0;
+
+	DynamicArray<RenderPassDependency> m_consumers;
+	DynamicArray<RenderPassDependency> m_producers;
+
+	BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS> m_consumerRtMask = {false};
+	BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS> m_producerRtMask = {false};
+	BitSet<MAX_RENDER_GRAPH_BUFFERS> m_consumerBufferMask = {false};
+	BitSet<MAX_RENDER_GRAPH_BUFFERS> m_producerBufferMask = {false};
+
+	String m_name;
+
+	RenderPassBase(Type t)
+		: m_type(t)
+	{
+	}
+
+	void setName(CString name)
+	{
+		m_name.create(m_alloc, (name.isEmpty()) ? "N/A" : name);
+	}
+};
+
+/// XXX
+class GraphicsRenderPassInfo : public RenderPassBase
+{
+	friend class RenderGraphDescription;
+
+public:
+	GraphicsRenderPassInfo()
+		: RenderPassBase(Type::GRAPHICS)
+	{
+	}
+
+	~GraphicsRenderPassInfo()
+	{
+	}
+
+	void setColorRenderTarget(U32 location, const RenderTargetReference& ref)
+	{
+		m_colorAttachments[location] = ref;
+		m_colorAttachmentCount = location + 1;
+	}
+
+	void setDepthStencilRenderTarget(const RenderTargetReference& ref)
+	{
+		m_depthStencilAttachment = ref;
+	}
+
+private:
+	Array<RenderTargetReference, MAX_COLOR_ATTACHMENTS> m_colorAttachments;
+	U32 m_colorAttachmentCount = 0;
+	RenderTargetReference m_depthStencilAttachment;
+};
+
+/// XXX
+class NoGraphicsRenderPassInfo : private RenderPassBase
+{
+private:
+	NoGraphicsRenderPassInfo()
+		: RenderPassBase(Type::NO_GRAPHICS)
+	{
+	}
+};
+
+/// XXX
+class RenderTarget
+{
+	friend class RenderGraphDescription;
+
+private:
+	TextureInitInfo m_initInfo;
+	TexturePtr m_importedTex;
+};
+
+/// XXX
+class RenderGraphDescription
+{
+	friend class RenderGraph;
+
+public:
+	RenderGraphDescription(StackAllocator<U8> alloc)
+		: m_alloc(alloc)
+	{
+	}
+
+	~RenderGraphDescription()
+	{
+		for(RenderPassBase* pass : m_passes)
+		{
+			m_alloc.deleteInstance(pass);
+		}
+		m_passes.destroy(m_alloc);
+
+		m_renderTargets.destroy(m_alloc);
+	}
+
+	/// Create a new graphics render pass.
+	GraphicsRenderPassInfo& newGraphicsRenderPass(CString name)
+	{
+		GraphicsRenderPassInfo* pass = m_alloc.newInstance<GraphicsRenderPassInfo>();
+		pass->m_alloc = m_alloc;
+		pass->setName(name);
+		m_passes.emplaceBack(m_alloc, pass);
+		return *pass;
+	}
+
+	/// XXX
+	U32 importRenderTarget(CString name, TexturePtr tex)
+	{
+		RenderTarget rt;
+		rt.m_importedTex = tex;
+		m_renderTargets.emplaceBack(m_alloc, rt);
+		return m_renderTargets.getSize() - 1;
+	}
+
+	/// XXX
+	U32 newRenderTarget(CString name, const TextureInitInfo& initInf)
+	{
+		RenderTarget rt;
+		rt.m_initInfo = initInf;
+		m_renderTargets.emplaceBack(m_alloc, rt);
+		return m_renderTargets.getSize() - 1;
+	}
+
+private:
+	StackAllocator<U8> m_alloc;
+	DynamicArray<RenderPassBase*> m_passes;
+	DynamicArray<RenderTarget> m_renderTargets;
+};
+
 /// XXX
 /// XXX
 class RenderGraph final : public GrObject
 class RenderGraph final : public GrObject
 {
 {
@@ -75,57 +303,30 @@ public:
 
 
 	/// @name 1st step methods
 	/// @name 1st step methods
 	/// @{
 	/// @{
-	RenderGraphHandle importRenderTarget(CString name, TexturePtr tex);
-
-	RenderGraphHandle newRenderTarget(const TextureInitInfo& texInf);
-
-	RenderGraphHandle importBuffer(CString name, BufferPtr buff);
-
-	RenderGraphHandle newBuffer(CString name, PtrSize size, BufferUsageBit usage, BufferMapAccessBit access);
-
-	RenderGraphHandle registerRenderPass(CString name,
-		WeakArray<RenderTargetInfo> colorAttachments,
-		RenderTargetInfo depthStencilAttachment,
-		WeakArray<RenderGraphDependency> consumers,
-		WeakArray<RenderGraphDependency> producers,
-		RenderGraphWorkCallback callback,
-		void* userData,
-		U32 secondLevelCmdbsCount);
-
-	/// For compute or other work (mipmap gen).
-	RenderGraphHandle registerNonRenderPass(CString name,
-		WeakArray<RenderGraphDependency> consumers,
-		WeakArray<RenderGraphDependency> producers,
-		RenderGraphWorkCallback callback,
-		void* userData);
+	void compileNewGraph(const RenderGraphDescription& descr);
 	/// @}
 	/// @}
 
 
 	/// @name 2nd step methods
 	/// @name 2nd step methods
 	/// @{
 	/// @{
-	void bake();
-	/// @}
-
-	/// @name 3rd step methods
-	/// @{
 
 
-	/// Will call a number of RenderGraphWorkCallback that populate 2nd level command buffers.
+	/// Will call a number of RenderPassWorkCallback that populate 2nd level command buffers.
 	void runSecondLevel();
 	void runSecondLevel();
 	/// @}
 	/// @}
 
 
-	/// @name 4th step methods
+	/// @name 3rd step methods
 	/// @{
 	/// @{
 
 
-	/// Will call a number of RenderGraphWorkCallback that populate 1st level command buffers.
+	/// Will call a number of RenderPassWorkCallback that populate 1st level command buffers.
 	void run();
 	void run();
 	/// @}
 	/// @}
 
 
-	/// @name 3rd and 4th step methods
+	/// @name 2nd and 3rd step methods
 	/// @{
 	/// @{
-	TexturePtr getTexture(RenderGraphHandle handle);
-	BufferPtr getBuffer(RenderGraphHandle handle);
+	TexturePtr getTexture(RenderTargetHandle handle);
+	BufferPtr getBuffer(RenderPassBufferHandle handle);
 	/// @}
 	/// @}
 
 
-	/// @name 5th step methods
+	/// @name 4th step methods
 	/// @{
 	/// @{
 
 
 	/// Reset the graph for a new frame. All previously created RenderGraphHandle are invalid after that call.
 	/// Reset the graph for a new frame. All previously created RenderGraphHandle are invalid after that call.
@@ -133,100 +334,31 @@ public:
 	/// @}
 	/// @}
 
 
 private:
 private:
-	static constexpr U MAX_PASSES = 128;
-	static constexpr U32 TEXTURE_TYPE = 0x10000000;
-	static constexpr U32 BUFFER_TYPE = 0x20000000;
-	static constexpr U32 RT_TYPE = 0x40000000;
-
-	GrManager* m_gr;
 	StackAllocator<U8> m_tmpAlloc;
 	StackAllocator<U8> m_tmpAlloc;
 
 
 	/// Render targets of the same type+size+format.
 	/// Render targets of the same type+size+format.
-	class RenderTargetCacheEntry
+	class RenderTargetCacheEntry : public IntrusiveHashMapEnabled<RenderTargetCacheEntry>
 	{
 	{
 	public:
 	public:
 		DynamicArray<TexturePtr> m_textures;
 		DynamicArray<TexturePtr> m_textures;
 		U32 m_texturesInUse = 0;
 		U32 m_texturesInUse = 0;
 	};
 	};
 
 
-	HashMap<TextureInitInfo, RenderTargetCacheEntry*> m_renderTargetCache; ///< Imported render targets.
+	IntrusiveHashMap<TextureInitInfo, RenderTargetCacheEntry> m_renderTargetCache; ///< Non-imported render targets.
 	HashMap<FramebufferInitInfo, FramebufferPtr> m_framebufferCache;
 	HashMap<FramebufferInitInfo, FramebufferPtr> m_framebufferCache;
 
 
 	// Forward declarations
 	// Forward declarations
-	class PassBatch;
-	class RenderTarget;
-	class Pass;
 	class BakeContext;
 	class BakeContext;
+	class Pass;
+	class Batch;
 
 
-	template<typename T>
-	class Storage
-	{
-	public:
-		T* m_arr = nullptr;
-		U32 m_count = 0;
-		U32 m_storage = 0;
-
-		T& operator[](U i)
-		{
-			ANKI_ASSERT(i < m_count);
-			return m_arr[i];
-		}
-
-		const T& operator[](U i) const
-		{
-			ANKI_ASSERT(i < m_count);
-			return m_arr[i];
-		}
-
-		T* begin()
-		{
-			return &m_arr[0];
-		}
-
-		const T* begin() const
-		{
-			return &m_arr[0];
-		}
-
-		T* end()
-		{
-			return &m_arr[m_count];
-		}
-
-		const T* end() const
-		{
-			return &m_arr[m_count];
-		}
-
-		U32 getSize() const
-		{
-			return m_count;
-		}
-
-		void pushBack(StackAllocator<U8>& alloc, T&& x);
-
-		void reset()
-		{
-			m_arr = nullptr;
-			m_count = 0;
-			m_storage = 0;
-		}
-	};
-
-	/// @name Runtime stuff
-	/// @{
-	Storage<RenderTarget> m_renderTargets;
-	Storage<Pass> m_passes;
-	/// @}
-
-	RenderGraphHandle pushRenderTarget(CString name, TexturePtr tex, Bool imported);
-
-	Bool passHasUnmetDependencies(const BakeContext& ctx, const Pass& pass) const;
-
-	static Bool passADependsOnB(const Pass& a, const Pass& b);
+	TexturePtr getOrCreateRenderTarget(const TextureInitInfo& initInf);
 
 
 	/// Dump the dependency graph into a file.
 	/// Dump the dependency graph into a file.
 	ANKI_USE_RESULT Error dumpDependencyDotFile(const BakeContext& ctx, CString path) const;
 	ANKI_USE_RESULT Error dumpDependencyDotFile(const BakeContext& ctx, CString path) const;
+
+	static Bool passADependsOnB(BakeContext& ctx, const RenderPassBase& a, const RenderPassBase& b);
+	static Bool passHasUnmetDependencies(const BakeContext& ctx, U32 passIdx);
 };
 };
 /// @}
 /// @}
 
 

+ 13 - 16
src/anki/gr/Texture.h

@@ -14,7 +14,7 @@ namespace anki
 /// @{
 /// @{
 
 
 /// Sampler initializer.
 /// Sampler initializer.
-class SamplerInitInfo : public GrBaseInitInfo
+class alignas(4) SamplerInitInfo : public GrBaseInitInfo
 {
 {
 public:
 public:
 	F32 m_minLod = -1000.0;
 	F32 m_minLod = -1000.0;
@@ -24,6 +24,7 @@ public:
 	CompareOperation m_compareOperation = CompareOperation::ALWAYS;
 	CompareOperation m_compareOperation = CompareOperation::ALWAYS;
 	I8 m_anisotropyLevel = 0;
 	I8 m_anisotropyLevel = 0;
 	Bool8 m_repeat = true; ///< Repeat or clamp.
 	Bool8 m_repeat = true; ///< Repeat or clamp.
+	U8 _m_padding[3] = {0, 0, 0};
 
 
 	SamplerInitInfo() = default;
 	SamplerInitInfo() = default;
 
 
@@ -34,15 +35,14 @@ public:
 
 
 	U64 computeHash() const
 	U64 computeHash() const
 	{
 	{
-		const U8* start = reinterpret_cast<const U8*>(&m_minLod);
-		const U8* end = reinterpret_cast<const U8*>(&m_repeat) + sizeof(m_repeat);
-		ANKI_ASSERT((end - start == 13) && "Class needs to be tightly packed since we hash it");
-		return anki::computeHash(start, end - start);
+		return anki::computeHash(this, sizeof(*this));
 	}
 	}
 };
 };
+static_assert(
+	sizeof(SamplerInitInfo) == sizeof(GrBaseInitInfo) + 16, "Class needs to be tightly packed since we hash it");
 
 
 /// Texture initializer.
 /// Texture initializer.
-class TextureInitInfo : public GrBaseInitInfo
+class alignas(4) TextureInitInfo : public GrBaseInitInfo
 {
 {
 public:
 public:
 	U32 m_width = 0;
 	U32 m_width = 0;
@@ -50,19 +50,21 @@ public:
 	U32 m_depth = 1; //< Relevant only for 3D textures.
 	U32 m_depth = 1; //< Relevant only for 3D textures.
 	U32 m_layerCount = 1; ///< Relevant only for texture arrays.
 	U32 m_layerCount = 1; ///< Relevant only for texture arrays.
 
 
-	TextureType m_type = TextureType::_2D;
-
 	TextureUsageBit m_usage = TextureUsageBit::NONE; ///< How the texture will be used.
 	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; ///< It's initial usage.
 
 
 	/// It's usual usage. That way you won't need to call CommandBuffer::informTextureXXXCurrentUsage() all the time.
 	/// It's usual usage. That way you won't need to call CommandBuffer::informTextureXXXCurrentUsage() all the time.
 	TextureUsageBit m_usageWhenEncountered = TextureUsageBit::NONE;
 	TextureUsageBit m_usageWhenEncountered = TextureUsageBit::NONE;
 
 
+	TextureType m_type = TextureType::_2D;
+
 	U8 m_mipmapsCount = 1;
 	U8 m_mipmapsCount = 1;
 
 
 	PixelFormat m_format;
 	PixelFormat m_format;
 	U8 m_samples = 1;
 	U8 m_samples = 1;
 
 
+	U8 _m_padding = 0;
+
 	SamplerInitInfo m_sampling;
 	SamplerInitInfo m_sampling;
 
 
 	TextureInitInfo() = default;
 	TextureInitInfo() = default;
@@ -75,16 +77,11 @@ public:
 
 
 	U64 computeHash() const
 	U64 computeHash() const
 	{
 	{
-		const U8* start = reinterpret_cast<const U8*>(&m_width);
-		const U8* end = reinterpret_cast<const U8*>(&m_samples) + sizeof(m_samples);
-		ANKI_ASSERT((end - start == 24) && "Class needs to be tightly packed since we hash it");
-
-		const U8* starts = reinterpret_cast<const U8*>(&m_sampling.m_minLod);
-		const U8* ends = reinterpret_cast<const U8*>(&m_sampling.m_repeat) + sizeof(m_sampling.m_repeat);
-
-		return appendHash(starts, ends - starts, anki::computeHash(start, end - start));
+		return anki::computeHash(this, sizeof(*this));
 	}
 	}
 };
 };
+static_assert(sizeof(TextureInitInfo) == sizeof(GrBaseInitInfo) + 28 + sizeof(SamplerInitInfo),
+	"Class needs to be tightly packed since we hash it");
 
 
 /// GPU texture
 /// GPU texture
 class Texture final : public GrObject
 class Texture final : public GrObject

+ 0 - 3
src/anki/renderer/Renderer.h

@@ -105,9 +105,6 @@ public:
 
 
 		StackAllocator<U8> m_alloc;
 		StackAllocator<U8> m_alloc;
 
 
-		RenderGraphHandle m_spotRt;
-		RenderGraphHandle m_omniRt;
-
 		ShadowMapping(const StackAllocator<U8>& alloc)
 		ShadowMapping(const StackAllocator<U8>& alloc)
 			: m_alloc(alloc)
 			: m_alloc(alloc)
 		{
 		{

+ 21 - 5
src/anki/util/BitSet.h

@@ -98,6 +98,18 @@ public:
 		return *this;
 		return *this;
 	}
 	}
 
 
+	/// Bitwise not of self.
+	BitSet operator~() const
+	{
+		BitSet out;
+		for(U i = 0; i < CHUNK_COUNT; ++i)
+		{
+			out.m_chunks[i] = ~m_chunks[i];
+		}
+		out.zeroUnusedBits();
+		return out;
+	}
+
 	Bool operator==(const BitSet& b) const
 	Bool operator==(const BitSet& b) const
 	{
 	{
 		return memcmp(&m_chunks[0], &b.m_chunks[0], sizeof(m_chunks)) == 0;
 		return memcmp(&m_chunks[0], &b.m_chunks[0], sizeof(m_chunks)) == 0;
@@ -137,11 +149,7 @@ public:
 	void setAll()
 	void setAll()
 	{
 	{
 		memset(m_chunks, 0xFF, sizeof(m_chunks));
 		memset(m_chunks, 0xFF, sizeof(m_chunks));
-
-		// Zero the unused bits
-		const ChunkType REMAINING_BITS = N - (CHUNK_COUNT - 1) * CHUNK_BIT_COUNT;
-		const ChunkType REMAINING_BITMASK = std::numeric_limits<ChunkType>::max() >> REMAINING_BITS;
-		m_chunks[CHUNK_COUNT - 1] ^= REMAINING_BITMASK;
+		zeroUnusedBits();
 	}
 	}
 
 
 	/// Unset a bit (set to zero) at the given position.
 	/// Unset a bit (set to zero) at the given position.
@@ -228,6 +236,14 @@ protected:
 		ANKI_ASSERT(high < CHUNK_COUNT);
 		ANKI_ASSERT(high < CHUNK_COUNT);
 		ANKI_ASSERT(low < CHUNK_BIT_COUNT);
 		ANKI_ASSERT(low < CHUNK_BIT_COUNT);
 	}
 	}
+
+	/// Zero the unused bits.
+	void zeroUnusedBits()
+	{
+		const ChunkType REMAINING_BITS = N - (CHUNK_COUNT - 1) * CHUNK_BIT_COUNT;
+		const ChunkType REMAINING_BITMASK = std::numeric_limits<ChunkType>::max() >> REMAINING_BITS;
+		m_chunks[CHUNK_COUNT - 1] ^= REMAINING_BITMASK;
+	}
 };
 };
 /// @}
 /// @}
 
 

+ 2 - 2
src/anki/util/Logger.cpp

@@ -125,9 +125,9 @@ void Logger::defaultSystemMessageHandler(void*, const Info& info)
 	}
 	}
 
 
 	const char* fmt = "%s[%s][%s]%s%s %s (%s:%d %s)%s\n";
 	const char* fmt = "%s[%s][%s]%s%s %s (%s:%d %s)%s\n";
-	if(runningFromATerminal())
+	if(!runningFromATerminal())
 	{
 	{
-		terminalColorBg = "";
+		terminalColor = "";
 		terminalColorBg = "";
 		terminalColorBg = "";
 		endTerminalColor = "";
 		endTerminalColor = "";
 	}
 	}

+ 69 - 1
tests/gr/Gr.cpp

@@ -1535,7 +1535,75 @@ ANKI_TEST(Gr, RenderGraph)
 {
 {
 	COMMON_BEGIN()
 	COMMON_BEGIN()
 
 
-	// TODO
+	StackAllocator<U8> alloc(allocAligned, nullptr, 2_KB);
+	RenderGraphDescription descr(alloc);
+	RenderGraphPtr rgraph = gr->newInstance<RenderGraph>();
+
+	TextureInitInfo texInf("sm_scratch");
+	texInf.m_width = texInf.m_height = 16;
+	texInf.m_usage = TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE | TextureUsageBit::SAMPLED_FRAGMENT;
+	texInf.m_format = PixelFormat(ComponentFormat::R8G8B8A8, TransformFormat::UNORM);
+
+	// SM
+	RenderTargetHandle smScratchRt = descr.newRenderTarget("sm_scratch", texInf);
+	{
+		GraphicsRenderPassInfo& pass = descr.newGraphicsRenderPass("SM");
+		pass.newConsumer({smScratchRt, TextureUsageBit::NONE});
+		pass.newProducer({smScratchRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE});
+	}
+
+	// SM to exponential SM
+	RenderTargetHandle smExpRt = descr.newRenderTarget("sm_exp", texInf);
+	{
+		GraphicsRenderPassInfo& pass = descr.newGraphicsRenderPass("ESM");
+		pass.newConsumer({smScratchRt, TextureUsageBit::SAMPLED_FRAGMENT});
+		pass.newConsumer({smExpRt, TextureUsageBit::SAMPLED_FRAGMENT});
+		pass.newProducer({smExpRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+	}
+
+	// SM 2nd batch
+	{
+		GraphicsRenderPassInfo& pass = descr.newGraphicsRenderPass("SM #2");
+		pass.newConsumer({smScratchRt, TextureUsageBit::SAMPLED_FRAGMENT});
+		pass.newProducer({smScratchRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE});
+	}
+
+	// SM to exponential SM 2nd batch
+	{
+		GraphicsRenderPassInfo& pass = descr.newGraphicsRenderPass("ESM #2");
+		pass.newConsumer({smScratchRt, TextureUsageBit::SAMPLED_FRAGMENT});
+		pass.newConsumer({smExpRt, TextureUsageBit::SAMPLED_FRAGMENT});
+		pass.newProducer({smExpRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+	}
+
+	// GI gbuff
+	RenderTargetHandle giGbuffNormRt = descr.newRenderTarget("GI gbuff norm", texInf);
+	RenderTargetHandle giGbuffDiffRt = descr.newRenderTarget("GI gbuff diff", texInf);
+	RenderTargetHandle giGbuffDepthRt = descr.newRenderTarget("GI gbuff depth", texInf);
+	{
+		GraphicsRenderPassInfo& pass = descr.newGraphicsRenderPass("GI gbuff");
+		pass.newConsumer({giGbuffNormRt, TextureUsageBit::NONE});
+		pass.newConsumer({giGbuffDepthRt, TextureUsageBit::NONE});
+		pass.newConsumer({giGbuffDiffRt, TextureUsageBit::NONE});
+
+		pass.newProducer({giGbuffNormRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass.newProducer({giGbuffDepthRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass.newProducer({giGbuffDiffRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+	}
+
+	// GI light
+	RenderTargetHandle giGiLightRt = descr.newRenderTarget("GI light", texInf);
+	{
+		GraphicsRenderPassInfo& pass = descr.newGraphicsRenderPass("GI light");
+		pass.newConsumer({giGiLightRt, TextureUsageBit::NONE});
+		pass.newConsumer({giGbuffNormRt, TextureUsageBit::SAMPLED_FRAGMENT});
+		pass.newConsumer({giGbuffDepthRt, TextureUsageBit::SAMPLED_FRAGMENT});
+		pass.newConsumer({giGbuffDiffRt, TextureUsageBit::SAMPLED_FRAGMENT});
+
+		pass.newConsumer({giGiLightRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+	}
+
+	rgraph->compileNewGraph(descr);
 
 
 	COMMON_END()
 	COMMON_END()
 }
 }