Browse Source

Making IR Vulkan debug friendly and hopefully faster

Panagiotis Christopoulos Charitos 9 years ago
parent
commit
7c4c89295d

BIN
engine_data/Plight.ankimesh


BIN
engine_data/Slight.ankimesh


BIN
engine_data/light_meshes.blend


+ 3 - 3
include/anki/gr/Common.h

@@ -160,11 +160,11 @@ public:
 };
 
 /// Compute max number of mipmaps for a 2D texture.
-inline U computeMaxMipmapCount(U w, U h)
+inline U computeMaxMipmapCount2d(U w, U h, U minSizeOfLastMip = 1)
 {
 	U s = (w < h) ? w : h;
 	U count = 0;
-	while(s)
+	while(s >= minSizeOfLastMip)
 	{
 		s /= 2;
 		++count;
@@ -174,7 +174,7 @@ inline U computeMaxMipmapCount(U w, U h)
 }
 
 /// Compute max number of mipmaps for a 3D texture.
-inline U computeMaxMipmapCount(U w, U h, U d)
+inline U computeMaxMipmapCount3d(U w, U h, U d)
 {
 	U s = (w < h) ? w : h;
 	s = (s < d) ? s : d;

+ 1 - 0
include/anki/gr/Enums.h

@@ -196,6 +196,7 @@ enum class TextureUsageBit : U16
 	/// @{
 	GENERATE_MIPMAPS = 1 << 10,
 	UPLOAD = 1 << 11,
+	CLEAR = 1 << 12 ///< Will be used in CommandBuffer::clearImage.
 	/// @}
 };
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(TextureUsageBit, inline)

+ 11 - 3
include/anki/renderer/Common.h

@@ -51,6 +51,14 @@ const U BLOOM_FRACTION = 4;
 /// IS mimap count is: log2(BLOOM_FRACTION)+2 extra mips for bloom+1
 const U IS_MIPMAP_COUNT = __builtin_popcount(BLOOM_FRACTION - 1) + 1 + 2;
 
+/// Get the number of mips for IS's render target.
+inline U getIsMipmapCount(U width, U height)
+{
+	U count = computeMaxMipmapCount2d(width, height, 512);
+	ANKI_ASSERT(count > 1);
+	return count;
+}
+
 /// Computes the 'a' and 'b' numbers for linearizeDepthOptimal
 inline void computeLinearizeDepthOptimal(F32 near, F32 far, F32& a, F32& b)
 {
@@ -60,16 +68,16 @@ inline void computeLinearizeDepthOptimal(F32 near, F32 far, F32& a, F32& b)
 
 const U MS_COLOR_ATTACHMENT_COUNT = 3;
 
-static const Array<PixelFormat, MS_COLOR_ATTACHMENT_COUNT>
+const Array<PixelFormat, MS_COLOR_ATTACHMENT_COUNT>
 	MS_COLOR_ATTACHMENT_PIXEL_FORMATS = {
 		{PixelFormat(ComponentFormat::R8G8B8A8, TransformFormat::UNORM),
 			PixelFormat(ComponentFormat::R8G8B8A8, TransformFormat::UNORM),
 			PixelFormat(ComponentFormat::R8G8B8A8, TransformFormat::UNORM)}};
 
-static const PixelFormat MS_DEPTH_ATTACHMENT_PIXEL_FORMAT(
+const PixelFormat MS_DEPTH_ATTACHMENT_PIXEL_FORMAT(
 	ComponentFormat::D24, TransformFormat::UNORM);
 
-static const PixelFormat IS_COLOR_ATTACHMENT_PIXEL_FORMAT(
+const PixelFormat IS_COLOR_ATTACHMENT_PIXEL_FORMAT(
 	ComponentFormat::R11G11B10, TransformFormat::FLOAT);
 /// @}
 

+ 74 - 23
include/anki/renderer/Ir.h

@@ -36,19 +36,19 @@ anki_internal:
 
 	ANKI_USE_RESULT Error run(RenderingContext& ctx);
 
-	U getCubemapArrayMipmapCount() const
+	U getReflectionTextureMipmapCount() const
 	{
-		return m_cubemapArrMipCount;
+		return m_is.m_lightRtMipCount;
 	}
 
 	TexturePtr getIrradianceTexture() const
 	{
-		return m_irradianceCubemapArr;
+		return m_irradiance.m_cubeArr;
 	}
 
 	TexturePtr getReflectionTexture() const
 	{
-		return m_envCubemapArr;
+		return m_is.m_lightRt;
 	}
 
 	TexturePtr getIntegrationLut() const
@@ -62,51 +62,102 @@ anki_internal:
 	}
 
 private:
+	class FaceInfo
+	{
+	public:
+		// MS
+		Array<TexturePtr, MS_COLOR_ATTACHMENT_COUNT> m_gbufferColorRts;
+		TexturePtr m_gbufferDepthRt;
+		FramebufferPtr m_msFb;
+
+		// IS
+		FramebufferPtr m_isFb;
+		ResourceGroupPtr m_plightRsrc;
+		ResourceGroupPtr m_slightRsrc;
+
+		// Irradiance
+		FramebufferPtr m_irradianceFb;
+
+		Bool created() const
+		{
+			return m_isFb.isCreated();
+		}
+	};
+
 	class CacheEntry
 	{
 	public:
 		const SceneNode* m_node = nullptr;
 		U64 m_nodeUuid;
 		Timestamp m_timestamp = 0; ///< When last accessed.
+
+		Array<FaceInfo, 6> m_faces;
 	};
 
 	static const U IRRADIANCE_TEX_SIZE = 32;
 	static const U MAX_PROBE_RENDERS_PER_FRAME = 1;
 
-	Renderer m_nestedR;
-	TexturePtr m_envCubemapArr;
-	U16 m_cubemapArrMipCount = 0;
 	U16 m_cubemapArrSize = 0;
 	U16 m_fbSize = 0;
-	DynamicArray<CacheEntry> m_cacheEntries;
+
+	// IS
+	class
+	{
+	public:
+		TexturePtr m_lightRt; ///< Cube array.
+		U32 m_lightRtMipCount = 0;
+
+		ShaderResourcePtr m_lightVert;
+		ShaderResourcePtr m_plightFrag;
+		ShaderResourcePtr m_slightFrag;
+		PipelinePtr m_plightPpline;
+		PipelinePtr m_slightPpline;
+
+		BufferPtr m_plightPositions;
+		BufferPtr m_plightIndices;
+		U32 m_plightIdxCount;
+		BufferPtr m_slightPositions;
+		BufferPtr m_slightIndices;
+		U32 m_slightIdxCount;
+	} m_is;
 
 	// Irradiance
-	TexturePtr m_irradianceCubemapArr;
-	ShaderResourcePtr m_computeIrradianceFrag;
-	PipelinePtr m_computeIrradiancePpline;
-	ResourceGroupPtr m_computeIrradianceResources;
+	class
+	{
+	public:
+		TexturePtr m_cubeArr;
+		U32 m_cubeArrMipCount = 0;
+
+		ShaderResourcePtr m_frag;
+		PipelinePtr m_ppline;
+		ResourceGroupPtr m_rsrc;
+	} m_irradiance;
+
+	DynamicArray<CacheEntry> m_cacheEntries;
 
 	// Other
 	TextureResourcePtr m_integrationLut;
 	SamplerPtr m_integrationLutSampler;
 
+	// Init
+	ANKI_USE_RESULT Error initIs();
 	ANKI_USE_RESULT Error initIrradiance();
+	void initFaceInfo(U cacheEntryIdx, U faceIdx);
+	ANKI_USE_RESULT Error loadMesh(
+		CString fname, BufferPtr& vert, BufferPtr& idx, U32& idxCount);
 
-	/// Bin probes in clusters.
-	void binProbes(U32 threadId, PtrSize threadsCount, IrRunContext& ctx);
-
+	// Rendering
 	ANKI_USE_RESULT Error tryRender(
 		RenderingContext& ctx, SceneNode& node, U& probesRendered);
 
-	void binProbe(U probeIdx, IrRunContext& ctx, IrTaskContext& task) const;
-
-	ANKI_USE_RESULT Error renderReflection(RenderingContext& ctx,
-		SceneNode& node,
-		ReflectionProbeComponent& reflc,
-		U cubemapIdx);
+	ANKI_USE_RESULT Error runMs(
+		RenderingContext& rctx, FrustumComponent& frc, U layer, U faceIdx);
+	void runIs(
+		RenderingContext& rctx, FrustumComponent& frc, U layer, U faceIdx);
+	void computeIrradiance(RenderingContext& rctx, U layer, U faceIdx);
 
-	static void writeIndicesAndCluster(
-		U clusterIdx, Bool hasPrevCluster, IrRunContext& ctx);
+	ANKI_USE_RESULT Error renderReflection(
+		RenderingContext& ctx, SceneNode& node, U cubemapIdx);
 
 	/// Find a cache entry to store the reflection.
 	void findCacheEntry(SceneNode& node, U& entry, Bool& render);

+ 0 - 2
include/anki/renderer/Is.h

@@ -23,8 +23,6 @@ class LightBin;
 class Is : public RenderingPass
 {
 anki_internal:
-	static const PixelFormat RT_PIXEL_FORMAT;
-
 	Is(Renderer* r);
 
 	~Is();

+ 29 - 12
shaders/Light.frag.glsl

@@ -3,15 +3,15 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#include "shaders/Packing.h"
+#include "shaders/Pack.glsl"
+#include "shaders/LightFunctions.glsl"
 
-layout(location = 0) vec3 in_posViewSpace;
-
-layout(location = 0) vec3 out_color;
+layout(location = 0) out vec3 out_color;
 
 // Point light
 struct PointLight
 {
+	vec4 projectionParams;
 	vec4 posRadius; // xyz: Light pos in eye space. w: The -1/radius
 	vec4 diffuseColorPad1; // xyz: diff color
 	vec4 specularColorPad1; // xyz: spec color
@@ -20,15 +20,17 @@ struct PointLight
 // Spot light
 struct SpotLight
 {
+	vec4 projectionParams;
 	vec4 posRadius; // xyz: Light pos in eye space. w: The -1/radius
 	vec4 diffuseColorOuterCos; // xyz: diff color, w: outer cosine of spot
 	vec4 specularColorInnerCos; // xyz: spec color, w: inner cosine of spot
-	vec4 lightDir;
-}
+	vec4 lightDirPad1;
+};
 
 layout(ANKI_TEX_BINDING(0, 0)) uniform sampler2D u_msRt0;
 layout(ANKI_TEX_BINDING(0, 1)) uniform sampler2D u_msRt1;
 layout(ANKI_TEX_BINDING(0, 2)) uniform sampler2D u_msRt2;
+layout(ANKI_TEX_BINDING(0, 3)) uniform sampler2D u_msDepthRt;
 
 layout(ANKI_UBO_BINDING(0, 1)) uniform u1_
 {
@@ -49,22 +51,37 @@ layout(ANKI_UBO_BINDING(0, 1)) uniform u1_
 #define u_lspec u_light.specularColorInnerCos.xyz
 #endif
 
+vec3 readPosition(in vec2 uv)
+{
+	vec3 fragPosVspace;
+
+	float depth = texture(u_msDepthRt, uv).r;
+	fragPosVspace.z =
+		u_light.projectionParams.z / (u_light.projectionParams.w + depth);
+
+	fragPosVspace.xy =
+		(2.0 * uv - 1.0) * u_light.projectionParams.xy * fragPosVspace.z;
+
+	return fragPosVspace;
+}
+
 void main()
 {
 	// Read G-buffer
 	vec2 uv = vec2(gl_FragCoord.xy) / vec2(RENDERING_WIDTH, RENDERING_HEIGHT);
 	GbufferInfo gbuffer;
 	readGBuffer(u_msRt0, u_msRt1, u_msRt2, uv, 0.0, gbuffer);
-	float a2 = pow(roughness, 2.0);
+	float a2 = pow(gbuffer.roughness, 2.0);
 
 	// Calculate the light color
-	vec3 viewDir = normalize(-in_posViewSpace);
-	vec3 frag2Light = u_light.posRadius.xyz - in_posViewSpace;
+	vec3 fragPos = readPosition(uv);
+	vec3 viewDir = normalize(-fragPos);
+	vec3 frag2Light = u_light.posRadius.xyz - fragPos;
 	vec3 l = normalize(frag2Light);
-	float nol = max(0.0, dot(normal, l));
+	float nol = max(0.0, dot(gbuffer.normal, l));
 
 	vec3 specC = computeSpecularColorBrdf(
-		viewDir, l, gbuffer.normal, gbuffer.specCol, u_lspec, a2, nol);
+		viewDir, l, gbuffer.normal, gbuffer.specular, u_lspec, a2, nol);
 
 	vec3 diffC = computeDiffuseColor(gbuffer.diffuse, u_ldiff);
 
@@ -77,7 +94,7 @@ void main()
 	float spot = computeSpotFactor(l,
 		u_light.diffuseColorOuterCos.w,
 		u_light.specularColorInnerCos.w,
-		u_light.lightDir.xyz);
+		u_light.lightDirPad1.xyz);
 
 	out_color = (diffC + specC) * (att * spot * lambert);
 #endif

+ 3 - 7
shaders/Light.vert.glsl

@@ -3,25 +3,21 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#include "shaders/Common.h"
+#include "shaders/Common.glsl"
 
-layout(location = 0) vec3 in_position;
-
-layout(location = 0) vec3 out_posViewSpace;
+layout(location = 0) in vec3 in_position;
 
 out gl_PerVertex
 {
 	vec4 gl_Position;
 };
 
-layout(ANKI_UBO_BINDING(0, 0)) uniform u0_
+layout(ANKI_UBO_BINDING(0, 0), row_major) uniform u0_
 {
 	mat4 u_mvp;
-	mat4 u_mv;
 };
 
 void main()
 {
 	gl_Position = u_mvp * vec4(in_position, 1.0);
-	out_posViewSpace = (u_mv * vec4(in_position, 1.0)).xyz;
 };

+ 0 - 2
shaders/Ssao.frag.glsl

@@ -64,8 +64,6 @@ float readZ(in vec2 uv)
 // Read position in view space
 vec3 readPosition(in vec2 uv)
 {
-	float depth = texture(u_mMsDepthRt, uv).r;
-
 	vec3 fragPosVspace;
 	fragPosVspace.z = readZ(uv);
 

+ 2 - 2
src/gr/gl/TextureImpl.cpp

@@ -299,12 +299,12 @@ void TextureImpl::init(const TextureInitInfo& init)
 	if(m_target != GL_TEXTURE_3D)
 	{
 		m_mipsCount = min<U>(
-			init.m_mipmapsCount, computeMaxMipmapCount(m_width, m_height));
+			init.m_mipmapsCount, computeMaxMipmapCount2d(m_width, m_height));
 	}
 	else
 	{
 		m_mipsCount = min<U>(init.m_mipmapsCount,
-			computeMaxMipmapCount(m_width, m_height, m_depth));
+			computeMaxMipmapCount3d(m_width, m_height, m_depth));
 	}
 
 	// Bind

+ 2 - 2
src/renderer/DownscaleBlur.cpp

@@ -4,8 +4,8 @@
 // http://www.anki3d.org/LICENSE
 
 #include <anki/renderer/DownscaleBlur.h>
-#include <anki/renderer/Is.h>
 #include <anki/renderer/Renderer.h>
+#include <anki/renderer/Is.h>
 
 namespace anki
 {
@@ -17,7 +17,7 @@ Error DownscaleBlur::initSubpass(U idx, const UVec2& inputTexSize)
 
 	PipelineInitInfo ppinit;
 	ppinit.m_color.m_attachmentCount = 1;
-	ppinit.m_color.m_attachments[0].m_format = Is::RT_PIXEL_FORMAT;
+	ppinit.m_color.m_attachments[0].m_format = IS_COLOR_ATTACHMENT_PIXEL_FORMAT;
 	ppinit.m_depthStencil.m_depthWriteEnabled = false;
 	ppinit.m_depthStencil.m_depthCompareFunction = CompareOperation::ALWAYS;
 

+ 1 - 1
src/renderer/Fs.cpp

@@ -28,7 +28,7 @@ Error Fs::init(const ConfigSet&)
 	// Create RT
 	m_r->createRenderTarget(m_width,
 		m_height,
-		Is::RT_PIXEL_FORMAT,
+		IS_COLOR_ATTACHMENT_PIXEL_FORMAT,
 		1,
 		SamplingFilter::NEAREST,
 		1,

+ 603 - 121
src/renderer/Ir.cpp

@@ -10,12 +10,48 @@
 #include <anki/scene/SceneNode.h>
 #include <anki/scene/Visibility.h>
 #include <anki/scene/FrustumComponent.h>
+#include <anki/scene/MoveComponent.h>
+#include <anki/scene/LightComponent.h>
 #include <anki/scene/ReflectionProbeComponent.h>
 #include <anki/core/Trace.h>
+#include <anki/resource/MeshLoader.h>
 
 namespace anki
 {
 
+//==============================================================================
+// Misc                                                                        =
+//==============================================================================
+
+class IrVertex
+{
+public:
+	Mat4 m_mvp;
+};
+
+class IrPointLight
+{
+public:
+	Vec4 m_projectionParams;
+	Vec4 m_posRadius;
+	Vec4 m_diffuseColorPad1;
+	Vec4 m_specularColorPad1;
+};
+
+class IrSpotLight
+{
+public:
+	Vec4 m_projectionParams;
+	Vec4 m_posRadius;
+	Vec4 m_diffuseColorOuterCos;
+	Vec4 m_specularColorInnerCos;
+	Vec4 m_lightDirPad1;
+};
+
+//==============================================================================
+// IR                                                                          =
+//==============================================================================
+
 //==============================================================================
 Ir::Ir(Renderer* r)
 	: RenderingPass(r)
@@ -50,92 +86,286 @@ Error Ir::init(const ConfigSet& config)
 
 	m_cacheEntries.create(getAllocator(), m_cubemapArrSize);
 
-	// Init the renderer
-	Config nestedRConfig;
-	nestedRConfig.set("dbg.enabled", false);
-	nestedRConfig.set("sm.enabled", false);
-	nestedRConfig.set("lf.maxFlares", 8);
-	nestedRConfig.set("pps.enabled", false);
-	nestedRConfig.set("renderingQuality", 1.0);
-	nestedRConfig.set("clusterSizeZ", 16);
-	nestedRConfig.set("width", m_fbSize);
-	nestedRConfig.set("height", m_fbSize);
-	nestedRConfig.set("lodDistance", 10.0);
-	nestedRConfig.set("samples", 1);
-	nestedRConfig.set("ir.enabled", false); // Very important to disable that
-	nestedRConfig.set("sslr.enabled", false); // And that
-
-	ANKI_CHECK(m_nestedR.init(&m_r->getThreadPool(),
-		&m_r->getResourceManager(),
-		&m_r->getGrManager(),
-		m_r->getAllocator(),
-		m_r->getFrameAllocator(),
-		nestedRConfig,
-		m_r->getGlobalTimestampPtr()));
-
-	// Init the textures
+	ANKI_CHECK(initIs());
+	ANKI_CHECK(initIrradiance());
+
+	// Load split sum integration LUT
+	ANKI_CHECK(getResourceManager().loadResource(
+		"engine_data/SplitSumIntegration.ankitex", m_integrationLut));
+
+	SamplerInitInfo sinit;
+	sinit.m_minMagFilter = SamplingFilter::LINEAR;
+	sinit.m_mipmapFilter = SamplingFilter::BASE;
+	sinit.m_minLod = 0.0;
+	sinit.m_maxLod = 1.0;
+	sinit.m_repeat = false;
+	m_integrationLutSampler = getGrManager().newInstance<Sampler>(sinit);
+
+	return ErrorCode::NONE;
+}
+
+//==============================================================================
+Error Ir::loadMesh(
+	CString fname, BufferPtr& vert, BufferPtr& idx, U32& idxCount)
+{
+	MeshLoader loader(&getResourceManager());
+	ANKI_CHECK(loader.load(fname));
+
+	PtrSize vertBuffSize =
+		loader.getHeader().m_totalVerticesCount * sizeof(Vec3);
+	vert = getGrManager().newInstance<Buffer>(vertBuffSize,
+		BufferUsageBit::VERTEX | BufferUsageBit::TRANSFER_DESTINATION,
+		BufferMapAccessBit::NONE);
+
+	idx = getGrManager().newInstance<Buffer>(loader.getIndexDataSize(),
+		BufferUsageBit::INDEX | BufferUsageBit::TRANSFER_DESTINATION,
+		BufferMapAccessBit::NONE);
+
+	// Upload data
+	CommandBufferInitInfo init;
+	init.m_flags = CommandBufferFlag::SMALL_BATCH;
+	CommandBufferPtr cmdb = getGrManager().newInstance<CommandBuffer>(init);
+
+	TransientMemoryToken token;
+	Vec3* verts =
+		static_cast<Vec3*>(getGrManager().allocateFrameTransientMemory(
+			vertBuffSize, BufferUsageBit::TRANSFER_SOURCE, token));
+
+	const U8* ptr = loader.getVertexData();
+	for(U i = 0; i < loader.getHeader().m_totalVerticesCount; ++i)
+	{
+		*verts = *reinterpret_cast<const Vec3*>(ptr);
+		++verts;
+		ptr += loader.getVertexSize();
+	}
+
+	cmdb->uploadBuffer(vert, 0, token);
+
+	void* cpuIds = getGrManager().allocateFrameTransientMemory(
+		loader.getIndexDataSize(), BufferUsageBit::TRANSFER_SOURCE, token);
+
+	memcpy(cpuIds, loader.getIndexData(), loader.getIndexDataSize());
+
+	cmdb->uploadBuffer(idx, 0, token);
+	idxCount = loader.getHeader().m_totalIndicesCount;
+
+	cmdb->flush();
+
+	return ErrorCode::NONE;
+}
+
+//==============================================================================
+void Ir::initFaceInfo(U cacheEntryIdx, U faceIdx)
+{
+	FaceInfo& face = m_cacheEntries[cacheEntryIdx].m_faces[faceIdx];
+	ANKI_ASSERT(!face.created());
+
 	TextureInitInfo texinit;
 
+	texinit.m_width = m_fbSize;
+	texinit.m_height = m_fbSize;
+	texinit.m_layerCount = 1;
+	texinit.m_depth = 1;
+	texinit.m_type = TextureType::_2D;
+	texinit.m_mipmapsCount = 1;
+	texinit.m_samples = 1;
+	texinit.m_sampling.m_minMagFilter = SamplingFilter::NEAREST;
+	texinit.m_sampling.m_mipmapFilter = SamplingFilter::NEAREST;
+	texinit.m_usage = TextureUsageBit::FRAGMENT_SHADER_SAMPLED
+		| TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE;
+
+	// Create color attachments
+	for(U i = 0; i < MS_COLOR_ATTACHMENT_COUNT; ++i)
+	{
+		texinit.m_format = MS_COLOR_ATTACHMENT_PIXEL_FORMATS[i];
+
+		face.m_gbufferColorRts[i] =
+			getGrManager().newInstance<Texture>(texinit);
+	}
+
+	// Create depth attachment
+	texinit.m_usage |= TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ;
+	texinit.m_format = MS_DEPTH_ATTACHMENT_PIXEL_FORMAT;
+	face.m_gbufferDepthRt = getGrManager().newInstance<Texture>(texinit);
+
+	// Create MS FB
+	FramebufferInitInfo fbInit;
+	fbInit.m_colorAttachmentCount = MS_COLOR_ATTACHMENT_COUNT;
+
+	for(U j = 0; j < MS_COLOR_ATTACHMENT_COUNT; ++j)
+	{
+		fbInit.m_colorAttachments[j].m_texture = face.m_gbufferColorRts[j];
+		fbInit.m_colorAttachments[j].m_loadOperation =
+			AttachmentLoadOperation::DONT_CARE;
+	}
+
+	fbInit.m_depthStencilAttachment.m_texture = face.m_gbufferDepthRt;
+	fbInit.m_depthStencilAttachment.m_loadOperation =
+		AttachmentLoadOperation::CLEAR;
+	fbInit.m_depthStencilAttachment.m_clearValue.m_depthStencil.m_depth = 1.0;
+
+	face.m_msFb = getGrManager().newInstance<Framebuffer>(fbInit);
+
+	// Create IS FB
+	ANKI_ASSERT(m_is.m_lightRt.isCreated());
+	fbInit = FramebufferInitInfo();
+	fbInit.m_colorAttachmentCount = 1;
+	fbInit.m_colorAttachments[0].m_texture = m_is.m_lightRt;
+	fbInit.m_colorAttachments[0].m_surface.m_layer = cacheEntryIdx;
+	fbInit.m_colorAttachments[0].m_surface.m_face = faceIdx;
+	fbInit.m_colorAttachments[0].m_loadOperation =
+		AttachmentLoadOperation::CLEAR;
+	fbInit.m_depthStencilAttachment.m_loadOperation =
+		AttachmentLoadOperation::LOAD;
+	fbInit.m_depthStencilAttachment.m_texture = face.m_gbufferDepthRt;
+
+	face.m_isFb = getGrManager().newInstance<Framebuffer>(fbInit);
+
+	// Create the IS resource group
+	ResourceGroupInitInfo rcinit;
+	rcinit.m_textures[0].m_texture = face.m_gbufferColorRts[0];
+	rcinit.m_textures[1].m_texture = face.m_gbufferColorRts[1];
+	rcinit.m_textures[2].m_texture = face.m_gbufferColorRts[2];
+	rcinit.m_textures[3].m_texture = face.m_gbufferDepthRt;
+
+	rcinit.m_uniformBuffers[0].m_uploadedMemory = true;
+	rcinit.m_uniformBuffers[1].m_uploadedMemory = true;
+
+	rcinit.m_vertexBuffers[0].m_buffer = m_is.m_plightPositions;
+	rcinit.m_indexBuffer.m_buffer = m_is.m_plightIndices;
+	rcinit.m_indexSize = 2;
+
+	face.m_plightRsrc = getGrManager().newInstance<ResourceGroup>(rcinit);
+
+	rcinit.m_vertexBuffers[0].m_buffer = m_is.m_slightPositions;
+	rcinit.m_indexBuffer.m_buffer = m_is.m_slightIndices;
+	face.m_slightRsrc = getGrManager().newInstance<ResourceGroup>(rcinit);
+
+	// Create irradiance FB
+	fbInit = FramebufferInitInfo();
+	fbInit.m_colorAttachmentCount = 1;
+	fbInit.m_colorAttachments[0].m_texture = m_irradiance.m_cubeArr;
+	fbInit.m_colorAttachments[0].m_surface.m_layer = cacheEntryIdx;
+	fbInit.m_colorAttachments[0].m_surface.m_face = faceIdx;
+	fbInit.m_colorAttachments[0].m_loadOperation =
+		AttachmentLoadOperation::DONT_CARE;
+
+	face.m_irradianceFb = getGrManager().newInstance<Framebuffer>(fbInit);
+}
+
+//==============================================================================
+Error Ir::initIs()
+{
+	m_is.m_lightRtMipCount = computeMaxMipmapCount2d(m_fbSize, m_fbSize, 4);
+
+	// Init texture
+	TextureInitInfo texinit;
 	texinit.m_width = m_fbSize;
 	texinit.m_height = m_fbSize;
 	texinit.m_layerCount = m_cubemapArrSize;
 	texinit.m_depth = 1;
 	texinit.m_type = TextureType::CUBE_ARRAY;
-	texinit.m_format = Is::RT_PIXEL_FORMAT;
-	texinit.m_mipmapsCount = MAX_U8;
+	texinit.m_mipmapsCount = m_is.m_lightRtMipCount;
 	texinit.m_samples = 1;
 	texinit.m_sampling.m_minMagFilter = SamplingFilter::LINEAR;
 	texinit.m_sampling.m_mipmapFilter = SamplingFilter::LINEAR;
+	texinit.m_usage = TextureUsageBit::FRAGMENT_SHADER_SAMPLED
+		| TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE
+		| TextureUsageBit::CLEAR;
+	texinit.m_format = IS_COLOR_ATTACHMENT_PIXEL_FORMAT;
 
-	m_envCubemapArr = getGrManager().newInstance<Texture>(texinit);
-
-	texinit.m_width = IRRADIANCE_TEX_SIZE;
-	texinit.m_height = IRRADIANCE_TEX_SIZE;
-	m_irradianceCubemapArr = getGrManager().newInstance<Texture>(texinit);
-
-	m_cubemapArrMipCount = computeMaxMipmapCount(m_fbSize, m_fbSize);
-
-	// Create irradiance stuff
-	ANKI_CHECK(initIrradiance());
+	m_is.m_lightRt = getGrManager().newInstance<Texture>(texinit);
 
-	// Clear the textures
+	// Clear the texture
 	CommandBufferInitInfo cinf;
 	CommandBufferPtr cmdb = getGrManager().newInstance<CommandBuffer>(cinf);
-	U irrMipCount =
-		computeMaxMipmapCount(IRRADIANCE_TEX_SIZE, IRRADIANCE_TEX_SIZE);
 	ClearValue clear;
 	for(U i = 0; i < m_cubemapArrSize; ++i)
 	{
 		for(U f = 0; f < 6; ++f)
 		{
-			for(U l = 0; l < m_cubemapArrMipCount; ++l)
+			for(U l = 0; l < m_is.m_lightRtMipCount; ++l)
 			{
-				// Do env
-				cmdb->clearTexture(
-					m_envCubemapArr, TextureSurfaceInfo(l, 0, f, i), clear);
-			}
+				TextureSurfaceInfo surf(l, 0, f, i);
 
-			for(U l = 0; l < irrMipCount; ++l)
-			{
-				cmdb->clearTexture(m_irradianceCubemapArr,
-					TextureSurfaceInfo(l, 0, f, i),
-					clear);
+				cmdb->setTextureBarrier(m_is.m_lightRt,
+					TextureUsageBit::NONE,
+					TextureUsageBit::CLEAR,
+					surf);
+
+				cmdb->clearTexture(m_is.m_lightRt, surf, clear);
+
+				cmdb->setTextureBarrier(m_is.m_lightRt,
+					TextureUsageBit::CLEAR,
+					TextureUsageBit::FRAGMENT_SHADER_SAMPLED,
+					surf);
 			}
 		}
 	}
 	cmdb->flush();
 
-	// Load split sum integration LUT
+	// Init shaders
 	ANKI_CHECK(getResourceManager().loadResource(
-		"engine_data/SplitSumIntegration.ankitex", m_integrationLut));
-
-	SamplerInitInfo sinit;
-	sinit.m_minMagFilter = SamplingFilter::LINEAR;
-	sinit.m_mipmapFilter = SamplingFilter::BASE;
-	sinit.m_minLod = 0.0;
-	sinit.m_maxLod = 1.0;
-	sinit.m_repeat = false;
-	m_integrationLutSampler = getGrManager().newInstance<Sampler>(sinit);
+		"shaders/Light.vert.glsl", m_is.m_lightVert));
+
+	StringAuto pps(getAllocator());
+	pps.sprintf("#define POINT_LIGHT\n"
+				"#define RENDERING_WIDTH %d\n"
+				"#define RENDERING_HEIGHT %d\n",
+		m_fbSize,
+		m_fbSize);
+
+	ANKI_CHECK(getResourceManager().loadResourceToCache(
+		m_is.m_plightFrag, "shaders/Light.frag.glsl", pps.toCString(), "r_"));
+
+	pps.destroy();
+	pps.sprintf("#define SPOT_LIGHT\n"
+				"#define RENDERING_WIDTH %d\n"
+				"#define RENDERING_HEIGHT %d\n",
+		m_fbSize,
+		m_fbSize);
+
+	ANKI_CHECK(getResourceManager().loadResourceToCache(
+		m_is.m_slightFrag, "shaders/Light.frag.glsl", pps.toCString(), "r_"));
+
+	// Init the pplines
+	PipelineInitInfo pinit;
+	pinit.m_vertex.m_attributeCount = 1;
+	pinit.m_vertex.m_attributes[0].m_format =
+		PixelFormat(ComponentFormat::R32G32B32, TransformFormat::FLOAT);
+	pinit.m_vertex.m_bindingCount = 1;
+	pinit.m_vertex.m_bindings[0].m_stride = sizeof(F32) * 3;
+
+	pinit.m_rasterizer.m_cullMode = CullMode::FRONT;
+
+	pinit.m_depthStencil.m_depthWriteEnabled = false;
+	pinit.m_depthStencil.m_depthCompareFunction = CompareOperation::GREATER;
+	pinit.m_depthStencil.m_format = MS_DEPTH_ATTACHMENT_PIXEL_FORMAT;
+
+	pinit.m_color.m_attachmentCount = 1;
+	auto& att = pinit.m_color.m_attachments[0];
+	att.m_format = IS_COLOR_ATTACHMENT_PIXEL_FORMAT;
+	att.m_srcBlendMethod = BlendMethod::ONE;
+	att.m_dstBlendMethod = BlendMethod::ONE;
+
+	pinit.m_shaders[ShaderType::VERTEX] = m_is.m_lightVert->getGrShader();
+	pinit.m_shaders[ShaderType::FRAGMENT] = m_is.m_plightFrag->getGrShader();
+
+	m_is.m_plightPpline = getGrManager().newInstance<Pipeline>(pinit);
+
+	pinit.m_shaders[ShaderType::FRAGMENT] = m_is.m_slightFrag->getGrShader();
+	m_is.m_slightPpline = getGrManager().newInstance<Pipeline>(pinit);
+
+	// Init vert/idx buffers
+	ANKI_CHECK(loadMesh("engine_data/Plight.ankimesh",
+		m_is.m_plightPositions,
+		m_is.m_plightIndices,
+		m_is.m_plightIdxCount));
+
+	ANKI_CHECK(loadMesh("engine_data/Slight.ankimesh",
+		m_is.m_slightPositions,
+		m_is.m_slightIndices,
+		m_is.m_slightIdxCount));
 
 	return ErrorCode::NONE;
 }
@@ -143,35 +373,334 @@ Error Ir::init(const ConfigSet& config)
 //==============================================================================
 Error Ir::initIrradiance()
 {
+	m_irradiance.m_cubeArrMipCount =
+		computeMaxMipmapCount2d(IRRADIANCE_TEX_SIZE, IRRADIANCE_TEX_SIZE, 4);
+
+	// Init texture
+	TextureInitInfo texinit;
+	texinit.m_width = IRRADIANCE_TEX_SIZE;
+	texinit.m_height = IRRADIANCE_TEX_SIZE;
+	texinit.m_layerCount = m_cubemapArrSize;
+	texinit.m_depth = 1;
+	texinit.m_type = TextureType::CUBE_ARRAY;
+	texinit.m_mipmapsCount = m_irradiance.m_cubeArrMipCount;
+	texinit.m_samples = 1;
+	texinit.m_sampling.m_minMagFilter = SamplingFilter::LINEAR;
+	texinit.m_sampling.m_mipmapFilter = SamplingFilter::LINEAR;
+	texinit.m_usage = TextureUsageBit::FRAGMENT_SHADER_SAMPLED
+		| TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE
+		| TextureUsageBit::CLEAR;
+	texinit.m_format = IS_COLOR_ATTACHMENT_PIXEL_FORMAT;
+
+	m_irradiance.m_cubeArr = getGrManager().newInstance<Texture>(texinit);
+
 	// Create the shader
 	StringAuto pps(getFrameAllocator());
 	pps.sprintf("#define CUBEMAP_SIZE %u\n", IRRADIANCE_TEX_SIZE);
 
-	ANKI_CHECK(getResourceManager().loadResourceToCache(m_computeIrradianceFrag,
+	ANKI_CHECK(getResourceManager().loadResourceToCache(m_irradiance.m_frag,
 		"shaders/Irradiance.frag.glsl",
 		pps.toCString(),
-		"r_ir_"));
+		"r_"));
 
 	// Create the ppline
 	ColorStateInfo colorInf;
 	colorInf.m_attachmentCount = 1;
-	colorInf.m_attachments[0].m_format = Is::RT_PIXEL_FORMAT;
+	colorInf.m_attachments[0].m_format = IS_COLOR_ATTACHMENT_PIXEL_FORMAT;
 
-	m_r->createDrawQuadPipeline(m_computeIrradianceFrag->getGrShader(),
-		colorInf,
-		m_computeIrradiancePpline);
+	m_r->createDrawQuadPipeline(
+		m_irradiance.m_frag->getGrShader(), colorInf, m_irradiance.m_ppline);
 
 	// Create the resources
 	ResourceGroupInitInfo rcInit;
 	rcInit.m_uniformBuffers[0].m_uploadedMemory = true;
-	rcInit.m_textures[0].m_texture = m_envCubemapArr;
+	rcInit.m_textures[0].m_texture = m_is.m_lightRt;
 
-	m_computeIrradianceResources =
-		getGrManager().newInstance<ResourceGroup>(rcInit);
+	m_irradiance.m_rsrc = getGrManager().newInstance<ResourceGroup>(rcInit);
+
+	// Clear texture
+	CommandBufferInitInfo cinf;
+	cinf.m_flags = CommandBufferFlag::SMALL_BATCH;
+	CommandBufferPtr cmdb = getGrManager().newInstance<CommandBuffer>(cinf);
+	ClearValue clear;
+	for(U i = 0; i < m_cubemapArrSize; ++i)
+	{
+		for(U f = 0; f < 6; ++f)
+		{
+			for(U l = 0; l < m_irradiance.m_cubeArrMipCount; ++l)
+			{
+				TextureSurfaceInfo surf(l, 0, f, i);
+
+				cmdb->setTextureBarrier(m_is.m_lightRt,
+					TextureUsageBit::NONE,
+					TextureUsageBit::CLEAR,
+					surf);
+
+				cmdb->clearTexture(m_irradiance.m_cubeArr, surf, clear);
+
+				cmdb->setTextureBarrier(m_is.m_lightRt,
+					TextureUsageBit::CLEAR,
+					TextureUsageBit::FRAGMENT_SHADER_SAMPLED,
+					surf);
+			}
+		}
+	}
+	cmdb->flush();
+
+	return ErrorCode::NONE;
+}
+
+//==============================================================================
+Error Ir::runMs(
+	RenderingContext& rctx, FrustumComponent& frc, U layer, U faceIdx)
+{
+	CommandBufferPtr& cmdb = rctx.m_commandBuffer;
+	VisibilityTestResults& vis = frc.getVisibilityTestResults();
+
+	FaceInfo& face = m_cacheEntries[layer].m_faces[faceIdx];
+
+	if(!face.created())
+	{
+		initFaceInfo(layer, faceIdx);
+	}
+
+	// Set barriers
+	for(U i = 0; i < MS_COLOR_ATTACHMENT_COUNT; ++i)
+	{
+		cmdb->setTextureBarrier(face.m_gbufferColorRts[i],
+			TextureUsageBit::NONE,
+			TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
+			TextureSurfaceInfo(0, 0, 0, 0));
+	}
+
+	cmdb->setTextureBarrier(face.m_gbufferDepthRt,
+		TextureUsageBit::NONE,
+		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
+		TextureSurfaceInfo(0, 0, 0, 0));
+
+	// Start render pass
+	cmdb->beginRenderPass(face.m_msFb);
+	cmdb->setViewport(0, 0, m_fbSize, m_fbSize);
+	cmdb->setPolygonOffset(0.0, 0.0);
+
+	/// Draw
+	ANKI_CHECK(m_r->getSceneDrawer().drawRange(Pass::MS_FS,
+		frc,
+		cmdb,
+		vis.getBegin(VisibilityGroupType::RENDERABLES_MS),
+		vis.getEnd(VisibilityGroupType::RENDERABLES_MS)));
+
+	// End and set barriers
+	cmdb->endRenderPass();
+
+	for(U i = 0; i < MS_COLOR_ATTACHMENT_COUNT; ++i)
+	{
+		cmdb->setTextureBarrier(face.m_gbufferColorRts[i],
+			TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
+			TextureUsageBit::FRAGMENT_SHADER_SAMPLED,
+			TextureSurfaceInfo(0, 0, 0, 0));
+	}
+
+	cmdb->setTextureBarrier(face.m_gbufferDepthRt,
+		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
+		TextureUsageBit::FRAGMENT_SHADER_SAMPLED
+			| TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ,
+		TextureSurfaceInfo(0, 0, 0, 0));
 
 	return ErrorCode::NONE;
 }
 
+//==============================================================================
+void Ir::runIs(
+	RenderingContext& rctx, FrustumComponent& frc, U layer, U faceIdx)
+{
+	CommandBufferPtr& cmdb = rctx.m_commandBuffer;
+	VisibilityTestResults& vis = frc.getVisibilityTestResults();
+	FaceInfo& face = m_cacheEntries[layer].m_faces[faceIdx];
+
+	// Set barriers
+	cmdb->setTextureBarrier(m_is.m_lightRt,
+		TextureUsageBit::NONE,
+		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
+		TextureSurfaceInfo(0, 0, faceIdx, layer));
+
+	cmdb->beginRenderPass(face.m_isFb);
+
+	// Process all lights
+	const Mat4& vpMat = frc.getViewProjectionMatrix();
+	const Mat4& vMat = frc.getViewMatrix();
+
+	cmdb->bindPipeline(m_is.m_plightPpline);
+
+	const VisibleNode* it = vis.getBegin(VisibilityGroupType::LIGHTS_POINT);
+	const VisibleNode* end = vis.getEnd(VisibilityGroupType::LIGHTS_POINT);
+	while(it != end)
+	{
+		const LightComponent& lightc =
+			it->m_node->getComponent<LightComponent>();
+		const MoveComponent& movec = it->m_node->getComponent<MoveComponent>();
+
+		TransientMemoryInfo transient;
+
+		// Update uniforms
+		IrVertex* vert = static_cast<IrVertex*>(
+			getGrManager().allocateFrameTransientMemory(sizeof(IrVertex),
+				BufferUsageBit::UNIFORM_ANY_SHADER,
+				transient.m_uniformBuffers[0]));
+
+		Mat4 modelM(movec.getWorldTransform().getOrigin().xyz1(),
+			movec.getWorldTransform().getRotation().getRotationPart(),
+			lightc.getRadius());
+
+		vert->m_mvp = vpMat * modelM;
+
+		IrPointLight* light = static_cast<IrPointLight*>(
+			getGrManager().allocateFrameTransientMemory(sizeof(IrPointLight),
+				BufferUsageBit::UNIFORM_ANY_SHADER,
+				transient.m_uniformBuffers[1]));
+
+		Vec4 pos = vMat * movec.getWorldTransform().getOrigin().xyz1();
+
+		light->m_projectionParams = frc.getProjectionParameters();
+		light->m_posRadius =
+			Vec4(pos.xyz(), 1.0 / (lightc.getRadius() * lightc.getRadius()));
+		light->m_diffuseColorPad1 = lightc.getDiffuseColor();
+		light->m_specularColorPad1 = lightc.getSpecularColor();
+
+		// Bind stuff
+		cmdb->bindResourceGroup(face.m_plightRsrc, 0, &transient);
+
+		// Draw
+		cmdb->drawElements(m_is.m_plightIdxCount);
+
+		++it;
+	}
+
+	cmdb->bindPipeline(m_is.m_slightPpline);
+
+	it = vis.getBegin(VisibilityGroupType::LIGHTS_SPOT);
+	end = vis.getEnd(VisibilityGroupType::LIGHTS_SPOT);
+	while(it != end)
+	{
+		const LightComponent& lightc =
+			it->m_node->getComponent<LightComponent>();
+		const MoveComponent& movec = it->m_node->getComponent<MoveComponent>();
+
+		// Compute the model matrix
+		//
+		Mat4 modelM(movec.getWorldTransform().getOrigin().xyz1(),
+			movec.getWorldTransform().getRotation().getRotationPart(),
+			1.0);
+
+		// Calc the scale of the cone
+		Mat4 scaleM(Mat4::getIdentity());
+		scaleM(0, 0) = tan(lightc.getOuterAngle() / 2.0) * lightc.getDistance();
+		scaleM(1, 1) = scaleM(0, 0);
+		scaleM(2, 2) = lightc.getDistance();
+
+		modelM = modelM * scaleM;
+
+		TransientMemoryInfo transient;
+
+		// Update vertex uniforms
+		IrVertex* vert = static_cast<IrVertex*>(
+			getGrManager().allocateFrameTransientMemory(sizeof(IrVertex),
+				BufferUsageBit::UNIFORM_ANY_SHADER,
+				transient.m_uniformBuffers[0]));
+
+		vert->m_mvp = vpMat * modelM;
+
+		// Update fragment uniforms
+		IrSpotLight* light = static_cast<IrSpotLight*>(
+			getGrManager().allocateFrameTransientMemory(sizeof(IrSpotLight),
+				BufferUsageBit::UNIFORM_ANY_SHADER,
+				transient.m_uniformBuffers[1]));
+
+		light->m_projectionParams = frc.getProjectionParameters();
+
+		Vec4 pos = vMat * movec.getWorldTransform().getOrigin().xyz1();
+		light->m_posRadius = Vec4(
+			pos.xyz(), 1.0 / (lightc.getDistance() * lightc.getDistance()));
+
+		light->m_diffuseColorOuterCos =
+			Vec4(lightc.getDiffuseColor().xyz(), lightc.getOuterAngleCos());
+
+		light->m_specularColorInnerCos =
+			Vec4(lightc.getSpecularColor().xyz(), lightc.getInnerAngleCos());
+
+		Vec3 lightDir = -movec.getWorldTransform().getRotation().getZAxis();
+		lightDir = vMat.getRotationPart() * lightDir;
+		light->m_lightDirPad1 = lightDir.xyz0();
+
+		// Bind stuff
+		cmdb->bindResourceGroup(face.m_slightRsrc, 0, &transient);
+
+		// Draw
+		cmdb->drawElements(m_is.m_slightIdxCount);
+
+		++it;
+	}
+
+	// Generate mips
+	cmdb->endRenderPass();
+
+	cmdb->setTextureBarrier(m_is.m_lightRt,
+		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
+		TextureUsageBit::GENERATE_MIPMAPS,
+		TextureSurfaceInfo(0, 0, faceIdx, layer));
+
+	cmdb->generateMipmaps(m_is.m_lightRt, 0, faceIdx, layer);
+
+	cmdb->setTextureBarrier(m_is.m_lightRt,
+		TextureUsageBit::GENERATE_MIPMAPS,
+		TextureUsageBit::FRAGMENT_SHADER_SAMPLED,
+		TextureSurfaceInfo(0, 0, faceIdx, layer));
+}
+
+//==============================================================================
+void Ir::computeIrradiance(RenderingContext& rctx, U layer, U faceIdx)
+{
+	CommandBufferPtr& cmdb = rctx.m_commandBuffer;
+	FaceInfo& face = m_cacheEntries[layer].m_faces[faceIdx];
+
+	// Set barrier
+	cmdb->setTextureBarrier(m_irradiance.m_cubeArr,
+		TextureUsageBit::NONE,
+		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
+		TextureSurfaceInfo(0, 0, faceIdx, layer));
+
+	// Set state and draw
+	cmdb->setViewport(0, 0, IRRADIANCE_TEX_SIZE, IRRADIANCE_TEX_SIZE);
+
+	TransientMemoryInfo dinf;
+	UVec4* faceIdxArrayIdx = static_cast<UVec4*>(
+		getGrManager().allocateFrameTransientMemory(sizeof(UVec4),
+			BufferUsageBit::UNIFORM_ANY_SHADER,
+			dinf.m_uniformBuffers[0]));
+	faceIdxArrayIdx->x() = faceIdx;
+	faceIdxArrayIdx->y() = layer;
+
+	cmdb->bindResourceGroup(m_irradiance.m_rsrc, 0, &dinf);
+	cmdb->bindPipeline(m_irradiance.m_ppline);
+	cmdb->beginRenderPass(face.m_irradianceFb);
+
+	m_r->drawQuad(cmdb);
+	cmdb->endRenderPass();
+
+	// Gen mips
+	cmdb->setTextureBarrier(m_irradiance.m_cubeArr,
+		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
+		TextureUsageBit::GENERATE_MIPMAPS,
+		TextureSurfaceInfo(0, 0, faceIdx, layer));
+
+	cmdb->generateMipmaps(m_irradiance.m_cubeArr, 0, faceIdx, layer);
+
+	cmdb->setTextureBarrier(m_irradiance.m_cubeArr,
+		TextureUsageBit::GENERATE_MIPMAPS,
+		TextureUsageBit::FRAGMENT_SHADER_SAMPLED,
+		TextureSurfaceInfo(0, 0, faceIdx, layer));
+}
+
 //==============================================================================
 Error Ir::run(RenderingContext& rctx)
 {
@@ -227,7 +756,7 @@ Error Ir::tryRender(RenderingContext& ctx, SceneNode& node, U& probesRendered)
 	{
 		++probesRendered;
 		reflc.setMarkedForRendering(false);
-		ANKI_CHECK(renderReflection(ctx, node, reflc, entry));
+		ANKI_CHECK(renderReflection(ctx, node, entry));
 	}
 
 	// If you need to render it mark it for the next frame
@@ -240,15 +769,11 @@ Error Ir::tryRender(RenderingContext& ctx, SceneNode& node, U& probesRendered)
 }
 
 //==============================================================================
-Error Ir::renderReflection(RenderingContext& ctx,
-	SceneNode& node,
-	ReflectionProbeComponent& reflc,
-	U cubemapIdx)
+Error Ir::renderReflection(RenderingContext& ctx, SceneNode& node, U cubemapIdx)
 {
 	ANKI_TRACE_INC_COUNTER(RENDERER_REFLECTIONS, 1);
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
 
-	// Get the frustum components
+	// Gather the frustum components
 	Array<FrustumComponent*, 6> frustumComponents;
 	U count = 0;
 	Error err = node.iterateComponentsOfType<FrustumComponent>(
@@ -262,52 +787,9 @@ Error Ir::renderReflection(RenderingContext& ctx,
 	// Render cubemap
 	for(U i = 0; i < 6; ++i)
 	{
-		// Render
-		RenderingContext nestedCtx(ctx.m_tempAllocator);
-		nestedCtx.m_frustumComponent = frustumComponents[i];
-		nestedCtx.m_commandBuffer = cmdb;
-
-		ANKI_CHECK(m_nestedR.render(nestedCtx));
-
-		// Copy env texture
-		cmdb->copyTextureToTexture(m_nestedR.getIs().getRt(),
-			TextureSurfaceInfo(0, 0, 0, 0),
-			m_envCubemapArr,
-			TextureSurfaceInfo(0, 0, i, cubemapIdx));
-
-		// Gen mips of env tex
-		cmdb->generateMipmaps(m_envCubemapArr, 0, i, cubemapIdx);
-	}
-
-	// Compute irradiance
-	cmdb->setViewport(0, 0, IRRADIANCE_TEX_SIZE, IRRADIANCE_TEX_SIZE);
-	for(U i = 0; i < 6; ++i)
-	{
-		TransientMemoryInfo dinf;
-		UVec4* faceIdxArrayIdx = static_cast<UVec4*>(
-			getGrManager().allocateFrameTransientMemory(sizeof(UVec4),
-				BufferUsageBit::UNIFORM_ANY_SHADER,
-				dinf.m_uniformBuffers[0]));
-		faceIdxArrayIdx->x() = i;
-		faceIdxArrayIdx->y() = cubemapIdx;
-
-		cmdb->bindResourceGroup(m_computeIrradianceResources, 0, &dinf);
-
-		FramebufferInitInfo fbinit;
-		fbinit.m_colorAttachmentCount = 1;
-		fbinit.m_colorAttachments[0].m_texture = m_irradianceCubemapArr;
-		fbinit.m_colorAttachments[0].m_surface.m_layer = cubemapIdx;
-		fbinit.m_colorAttachments[0].m_surface.m_face = i;
-		fbinit.m_colorAttachments[0].m_loadOperation =
-			AttachmentLoadOperation::DONT_CARE;
-		FramebufferPtr fb = getGrManager().newInstance<Framebuffer>(fbinit);
-		cmdb->beginRenderPass(fb);
-
-		cmdb->bindPipeline(m_computeIrradiancePpline);
-		m_r->drawQuad(cmdb);
-		cmdb->endRenderPass();
-
-		cmdb->generateMipmaps(m_irradianceCubemapArr, 0, i, cubemapIdx);
+		ANKI_CHECK(runMs(ctx, *frustumComponents[i], cubemapIdx, i));
+		runIs(ctx, *frustumComponents[i], cubemapIdx, i);
+		computeIrradiance(ctx, cubemapIdx, i);
 	}
 
 	return ErrorCode::NONE;

+ 4 - 6
src/renderer/Is.cpp

@@ -35,9 +35,6 @@ struct ShaderCommonUniforms
 // Is                                                                          =
 //==============================================================================
 
-const PixelFormat Is::RT_PIXEL_FORMAT(
-	ComponentFormat::R11G11B10, TransformFormat::FLOAT);
-
 //==============================================================================
 Is::Is(Renderer* r)
 	: RenderingPass(r)
@@ -110,7 +107,8 @@ Error Is::initInternal(const ConfigSet& config)
 		m_maxLightIds,
 		m_r->getSmEnabled() ? m_r->getSm().getPoissonEnabled() : 0,
 		m_r->getIrEnabled(),
-		(m_r->getIrEnabled()) ? m_r->getIr().getCubemapArrayMipmapCount() : 0);
+		(m_r->getIrEnabled()) ? m_r->getIr().getReflectionTextureMipmapCount()
+							  : 0);
 
 	// point light
 	ANKI_CHECK(getResourceManager().loadResourceToCache(
@@ -125,7 +123,7 @@ Error Is::initInternal(const ConfigSet& config)
 	init.m_depthStencil.m_depthWriteEnabled = false;
 	init.m_depthStencil.m_depthCompareFunction = CompareOperation::ALWAYS;
 	init.m_color.m_attachmentCount = 1;
-	init.m_color.m_attachments[0].m_format = RT_PIXEL_FORMAT;
+	init.m_color.m_attachments[0].m_format = IS_COLOR_ATTACHMENT_PIXEL_FORMAT;
 	init.m_shaders[U(ShaderType::VERTEX)] = m_lightVert->getGrShader();
 	init.m_shaders[U(ShaderType::FRAGMENT)] = m_lightFrag->getGrShader();
 	m_lightPpline = getGrManager().newInstance<Pipeline>(init);
@@ -135,7 +133,7 @@ Error Is::initInternal(const ConfigSet& config)
 	//
 	m_r->createRenderTarget(m_r->getWidth(),
 		m_r->getHeight(),
-		RT_PIXEL_FORMAT,
+		IS_COLOR_ATTACHMENT_PIXEL_FORMAT,
 		1,
 		SamplingFilter::LINEAR,
 		IS_MIPMAP_COUNT,

+ 1 - 2
src/renderer/Lf.cpp

@@ -5,7 +5,6 @@
 
 #include <anki/renderer/Lf.h>
 #include <anki/renderer/Bloom.h>
-#include <anki/renderer/Is.h>
 #include <anki/renderer/Ms.h>
 #include <anki/renderer/Renderer.h>
 #include <anki/scene/SceneGraph.h>
@@ -85,7 +84,7 @@ Error Lf::initSprite(const ConfigSet& config)
 	init.m_depthStencil.m_depthWriteEnabled = false;
 	init.m_depthStencil.m_depthCompareFunction = CompareOperation::ALWAYS;
 	init.m_color.m_attachmentCount = 1;
-	init.m_color.m_attachments[0].m_format = Is::RT_PIXEL_FORMAT;
+	init.m_color.m_attachments[0].m_format = IS_COLOR_ATTACHMENT_PIXEL_FORMAT;
 	init.m_color.m_attachments[0].m_srcBlendMethod = BlendMethod::ONE;
 	init.m_color.m_attachments[0].m_dstBlendMethod = BlendMethod::ONE;
 	init.m_shaders[U(ShaderType::VERTEX)] = m_realVert->getGrShader();

+ 1 - 1
src/renderer/Upsample.cpp

@@ -70,7 +70,7 @@ Error Upsample::init(const ConfigSet& config)
 	ppinit.m_depthStencil.m_depthCompareFunction = CompareOperation::ALWAYS;
 
 	ppinit.m_color.m_attachmentCount = 1;
-	ppinit.m_color.m_attachments[0].m_format = Is::RT_PIXEL_FORMAT;
+	ppinit.m_color.m_attachments[0].m_format = IS_COLOR_ATTACHMENT_PIXEL_FORMAT;
 	ppinit.m_color.m_attachments[0].m_srcBlendMethod = BlendMethod::ONE;
 	ppinit.m_color.m_attachments[0].m_dstBlendMethod = BlendMethod::SRC_ALPHA;
 

+ 1 - 1
src/resource/Model.cpp

@@ -242,7 +242,7 @@ void ModelPatch::computePipelineInitInfo(
 	else if(m_mtl->getForwardShading())
 	{
 		color.m_attachmentCount = 1;
-		color.m_attachments[0].m_format = Is::RT_PIXEL_FORMAT;
+		color.m_attachments[0].m_format = IS_COLOR_ATTACHMENT_PIXEL_FORMAT;
 		color.m_attachments[0].m_srcBlendMethod = BlendMethod::SRC_ALPHA;
 		color.m_attachments[0].m_dstBlendMethod =
 			BlendMethod::ONE_MINUS_SRC_ALPHA;

+ 1 - 2
src/resource/ParticleEmitterResource.cpp

@@ -9,7 +9,6 @@
 #include <anki/util/StringList.h>
 #include <anki/misc/Xml.h>
 #include <anki/renderer/Ms.h>
-#include <anki/renderer/Is.h>
 #include <cstring>
 
 namespace anki
@@ -249,7 +248,7 @@ Error ParticleEmitterResource::load(const ResourceFilename& filename)
 	pinit.m_depthStencil.m_format = Ms::DEPTH_RT_PIXEL_FORMAT;
 
 	pinit.m_color.m_attachmentCount = 1;
-	pinit.m_color.m_attachments[0].m_format = Is::RT_PIXEL_FORMAT;
+	pinit.m_color.m_attachments[0].m_format = IS_COLOR_ATTACHMENT_PIXEL_FORMAT;
 	pinit.m_color.m_attachments[0].m_srcBlendMethod = BlendMethod::SRC_ALPHA;
 	pinit.m_color.m_attachments[0].m_dstBlendMethod =
 		BlendMethod::ONE_MINUS_SRC_ALPHA;

+ 4 - 2
tools/blender_anki_additions.patch

@@ -108,7 +108,7 @@ index 13dc1ed..77d6a6e 100644
  	ep.addProfileElements();
  	bool twoSided = false;
 diff --git a/source/blender/collada/GeometryExporter.cpp b/source/blender/collada/GeometryExporter.cpp
-index 7c7c57f..d4c2df0 100644
+index 7c7c57f..00c909d 100644
 --- a/source/blender/collada/GeometryExporter.cpp
 +++ b/source/blender/collada/GeometryExporter.cpp
 @@ -47,6 +47,7 @@ extern "C" {
@@ -119,7 +119,7 @@ index 7c7c57f..d4c2df0 100644
  }
  
  #include "collada_internal.h"
-@@ -143,13 +144,125 @@ void GeometryExporter::operator()(Object *ob)
+@@ -143,13 +144,127 @@ void GeometryExporter::operator()(Object *ob)
  			createPolylist(0, has_uvs, has_color, ob, me, geom_id, norind);
  		}
  	}
@@ -136,6 +136,8 @@ index 7c7c57f..d4c2df0 100644
 +			"skip",
 +			"reflection_probe",
 +			"reflection_proxy",
++			"occluder",
++			"collision_mesh",
 +			NULL};
 +
 +		ID *mesh_id = (ID*)ob->data;

+ 1 - 1
tools/blender_anki_additions_readme.txt

@@ -4,4 +4,4 @@ Follow the blender build instuctions. Use the following to build the external:
 
 The command above will give CMAKE options. Add this as well -DOPENCOLLADA_ROOT_DIR
 
-Commit the patch is based upon: 2683dd8b93d6c14bb8b3bfa03650cd619d9dbb30
+Commit the patch is based upon: c05363e8895a8cd6daa2241706d357c47ed4a83e