Browse Source

Add IS shader permutation support

Panagiotis Christopoulos Charitos 8 years ago
parent
commit
7138ef050e

+ 23 - 3
shaders/Is.frag.glsl

@@ -164,7 +164,8 @@ void main()
 	// Shadowpass sample count
 	uint shadowSampleCount = computeShadowSampleCount(SHADOW_SAMPLE_COUNT, fragPos.z);
 
-	// Decals
+// Decals
+#if 1
 	uint count = u_lightIndices[idxOffset++];
 	while(count-- != 0)
 	{
@@ -172,6 +173,10 @@ void main()
 
 		appendDecalColors(decal, fragPos, diffCol, roughness);
 	}
+#else
+	uint count = u_lightIndices[idxOffset];
+	idxOffset += count;
+#endif
 
 	// Don't allow zero a2 because we may end up with division with zero
 	float a2 = roughness * 0.9 + 0.1;
@@ -180,7 +185,8 @@ void main()
 	// Ambient and emissive color
 	vec3 outC = diffCol * emission;
 
-	// Point lights
+// Point lights
+#if PERMUTATION & 1
 	count = u_lightIndices[idxOffset++];
 	while(count-- != 0)
 	{
@@ -198,8 +204,13 @@ void main()
 
 		outC += (specC + diffC) * (att * max(subsurface, lambert));
 	}
+#else
+	count = u_lightIndices[idxOffset];
+	idxOffset += count + 1;
+#endif
 
-	// Spot lights
+// Spot lights
+#if PERMUTATION & 2
 	count = u_lightIndices[idxOffset++];
 	while(count-- != 0)
 	{
@@ -219,6 +230,10 @@ void main()
 
 		outC += (diffC + specC) * (att * spot * max(subsurface, lambert));
 	}
+#else
+	count = u_lightIndices[idxOffset];
+	idxOffset += count + 1;
+#endif
 
 #if INDIRECT_ENABLED
 	vec3 eye = -viewDir;
@@ -240,6 +255,11 @@ void main()
 #endif
 
 	out_color = outC;
+
+#if 0
+	out_color = vec3(((PERMUTATION & 2) != 0) ? 1.0 : 0.0, ((PERMUTATION & 1) != 0) ? 1.0 : 0.0, 0.0);
+#endif
+
 #if 0
 	count = scount;
 	if(count == 0)

+ 10 - 0
shaders/MsStencilBufferIsPermutations.frag.glsl

@@ -0,0 +1,10 @@
+// Copyright (C) 2009-2017, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include "shaders/Common.glsl"
+
+void main()
+{
+}

+ 18 - 0
shaders/MsStencilBufferIsPermutations.vert.glsl

@@ -0,0 +1,18 @@
+// Copyright (C) 2009-2017, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include "shaders/Common.glsl"
+
+layout(location = 0) in vec3 in_position;
+layout(location = 1) in vec4 in_mvp0;
+layout(location = 2) in vec4 in_mvp1;
+layout(location = 3) in vec4 in_mvp2;
+layout(location = 4) in vec4 in_mvp3;
+
+void main()
+{
+	mat4 mvp = mat4(in_mvp0, in_mvp1, in_mvp2, in_mvp3);
+	ANKI_WRITE_POSITION(mvp * vec4(in_position, 1.0));
+}

+ 9 - 5
src/anki/gr/CommandBuffer.h

@@ -172,22 +172,26 @@ public:
 	/// Set depth offset and units. Set zeros to both to disable it.
 	void setPolygonOffset(F32 factor, F32 units);
 
-	/// Set stencil operations. To disable stencil test put StencilOperation::KEEP to all operations.
+	/// Set stencil operations. The default is StencilOperation::KEEP for all operations.
+	/// @note To disable stencil test put StencilOperation::KEEP to all operations and set setStencilCompareOperation
+	///       to CompareOperation::ALWAYS.
 	void setStencilOperations(FaceSelectionBit face,
 		StencilOperation stencilFail,
 		StencilOperation stencilPassDepthFail,
 		StencilOperation stencilPassDepthPass);
 
-	/// Set stencil compare operation.
+	/// Set stencil compare operation. The default comp is CompareOperation::ALWAYS.
+	/// @note To disable stencil test set the comp to CompareOperation::ALWAYS and set setStencilOperations to
+	///       StencilOperation::KEEP, StencilOperation::KEEP, StencilOperation::KEEP.
 	void setStencilCompareOperation(FaceSelectionBit face, CompareOperation comp);
 
-	/// Set the stencil compare mask.
+	/// Set the stencil compare mask. The default mask is MAX_U32.
 	void setStencilCompareMask(FaceSelectionBit face, U32 mask);
 
-	/// Set the stencil write mask.
+	/// Set the stencil write mask. The default mask is MAX_U32.
 	void setStencilWriteMask(FaceSelectionBit face, U32 mask);
 
-	/// Set the stencil reference.
+	/// Set the stencil reference. The default ref is 0.
 	void setStencilReference(FaceSelectionBit face, U32 ref);
 
 	/// Enable/disable depth write. To disable depth testing set depth write to false and depth compare operation to

+ 2 - 0
src/anki/gr/gl/ShaderImpl.cpp

@@ -49,6 +49,8 @@ static const char* SHADER_HEADER = R"(#version %u %s
 #if 1
 #extension GL_ARB_shader_ballot : require
 #define ANKI_ARB_SHADER_BALLOT 1
+#extension GL_ARB_shader_group_vote : require
+#define ANKI_ARB_SHADER_GROUP_VOTE 1
 #endif
 
 %s)";

+ 9 - 0
src/anki/renderer/Common.h

@@ -80,6 +80,15 @@ const PixelFormat IS_COLOR_ATTACHMENT_PIXEL_FORMAT(ComponentFormat::R11G11B10, T
 const PixelFormat FS_COLOR_ATTACHMENT_PIXEL_FORMAT(ComponentFormat::R16G16B16A16, TransformFormat::FLOAT);
 
 const PixelFormat DBG_COLOR_ATTACHMENT_PIXEL_FORMAT(ComponentFormat::R8G8B8, TransformFormat::UNORM);
+
+enum class IsShaderPermutationOption
+{
+	POINT_LIGHTS,
+	SPOT_LIGHTS,
+	COUNT
+};
+
+const U IS_SHADER_PERMOUTATION_COUNT = 1u << U(IsShaderPermutationOption::COUNT); // 2^IsShaderPermutationOption::COUNT
 /// @}
 
 } // end namespace anki

+ 6 - 58
src/anki/renderer/Ir.cpp

@@ -105,51 +105,6 @@ Error Ir::initInternal(const ConfigSet& config)
 	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::BUFFER_UPLOAD_DESTINATION, BufferMapAccessBit::NONE);
-
-	idx = getGrManager().newInstance<Buffer>(loader.getIndexDataSize(),
-		BufferUsageBit::INDEX | BufferUsageBit::BUFFER_UPLOAD_DESTINATION,
-		BufferMapAccessBit::NONE);
-
-	// Upload data
-	CommandBufferInitInfo init;
-	init.m_flags = CommandBufferFlag::SMALL_BATCH;
-	CommandBufferPtr cmdb = getGrManager().newInstance<CommandBuffer>(init);
-
-	StagingGpuMemoryToken token;
-	Vec3* verts = static_cast<Vec3*>(
-		m_r->getStagingGpuMemoryManager().allocateFrame(vertBuffSize, StagingGpuMemoryType::TRANSFER, 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->copyBufferToBuffer(token.m_buffer, token.m_offset, vert, 0, token.m_range);
-
-	void* cpuIds = m_r->getStagingGpuMemoryManager().allocateFrame(
-		loader.getIndexDataSize(), StagingGpuMemoryType::TRANSFER, token);
-
-	memcpy(cpuIds, loader.getIndexData(), loader.getIndexDataSize());
-
-	cmdb->copyBufferToBuffer(token.m_buffer, token.m_offset, idx, 0, token.m_range);
-	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];
@@ -270,13 +225,6 @@ Error Ir::initIs()
 	m_is.m_slightProg =
 		getGrManager().newInstance<ShaderProgram>(m_is.m_lightVert->getGrShader(), m_is.m_slightFrag->getGrShader());
 
-	// 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;
 }
 
@@ -400,8 +348,8 @@ void Ir::runIs(RenderingContext& rctx, FrustumComponent& frc, U layer, U faceIdx
 	const Mat4& vMat = frc.getViewMatrix();
 
 	cmdb->bindShaderProgram(m_is.m_plightProg);
-	cmdb->bindVertexBuffer(0, m_is.m_plightPositions, 0, sizeof(F32) * 3);
-	cmdb->bindIndexBuffer(m_is.m_plightIndices, 0, IndexType::U16);
+	cmdb->bindVertexBuffer(0, m_r->getLightVolumePrimitives().m_pointLightPositions, 0, sizeof(F32) * 3);
+	cmdb->bindIndexBuffer(m_r->getLightVolumePrimitives().m_pointLightIndices, 0, IndexType::U16);
 
 	const VisibleNode* it = vis.getBegin(VisibilityGroupType::LIGHTS_POINT);
 	const VisibleNode* end = vis.getEnd(VisibilityGroupType::LIGHTS_POINT);
@@ -429,14 +377,14 @@ void Ir::runIs(RenderingContext& rctx, FrustumComponent& frc, U layer, U faceIdx
 		light->m_specularColorPad1 = lightc.getSpecularColor();
 
 		// Draw
-		cmdb->drawElements(PrimitiveTopology::TRIANGLES, m_is.m_plightIdxCount);
+		cmdb->drawElements(PrimitiveTopology::TRIANGLES, m_r->getLightVolumePrimitives().m_pointLightIndexCount);
 
 		++it;
 	}
 
 	cmdb->bindShaderProgram(m_is.m_slightProg);
-	cmdb->bindVertexBuffer(0, m_is.m_slightPositions, 0, sizeof(F32) * 3);
-	cmdb->bindIndexBuffer(m_is.m_slightIndices, 0, IndexType::U16);
+	cmdb->bindVertexBuffer(0, m_r->getLightVolumePrimitives().m_spotLightPositions, 0, sizeof(F32) * 3);
+	cmdb->bindIndexBuffer(m_r->getLightVolumePrimitives().m_spotLightIndices, 0, IndexType::U16);
 
 	it = vis.getBegin(VisibilityGroupType::LIGHTS_SPOT);
 	end = vis.getEnd(VisibilityGroupType::LIGHTS_SPOT);
@@ -480,7 +428,7 @@ void Ir::runIs(RenderingContext& rctx, FrustumComponent& frc, U layer, U faceIdx
 		light->m_lightDirPad1 = lightDir.xyz0();
 
 		// Draw
-		cmdb->drawElements(PrimitiveTopology::TRIANGLES, m_is.m_slightIdxCount);
+		cmdb->drawElements(PrimitiveTopology::TRIANGLES, m_r->getLightVolumePrimitives().m_spotLightIndexCount);
 
 		++it;
 	}

+ 0 - 8
src/anki/renderer/Ir.h

@@ -110,13 +110,6 @@ private:
 		ShaderResourcePtr m_slightFrag;
 		ShaderProgramPtr m_plightProg;
 		ShaderProgramPtr m_slightProg;
-
-		BufferPtr m_plightPositions;
-		BufferPtr m_plightIndices;
-		U32 m_plightIdxCount;
-		BufferPtr m_slightPositions;
-		BufferPtr m_slightIndices;
-		U32 m_slightIdxCount;
 	} m_is;
 
 	// Irradiance
@@ -141,7 +134,6 @@ private:
 	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);
 
 	// Rendering
 	ANKI_USE_RESULT Error tryRender(RenderingContext& ctx, SceneNode& node, U& probesRendered);

+ 56 - 15
src/anki/renderer/Is.cpp

@@ -94,31 +94,50 @@ Error Is::initInternal(const ConfigSet& config)
 	//
 	// Load shaders and programs
 	//
+	static const char* DEFINES = "#define CLUSTER_COUNT_X %u\n"
+								 "#define CLUSTER_COUNT_Y %u\n"
+								 "#define CLUSTER_COUNT %u\n"
+								 "#define RENDERER_WIDTH %u\n"
+								 "#define RENDERER_HEIGHT %u\n"
+								 "#define POISSON %u\n"
+								 "#define INDIRECT_ENABLED %u\n"
+								 "#define IR_MIPMAP_COUNT %u\n"
+								 "#define PERMUTATION %u";
+
 	StringAuto pps(getAllocator());
 
-	pps.sprintf("#define CLUSTER_COUNT_X %u\n"
-				"#define CLUSTER_COUNT_Y %u\n"
-				"#define CLUSTER_COUNT %u\n"
-				"#define RENDERER_WIDTH %u\n"
-				"#define RENDERER_HEIGHT %u\n"
-				"#define MAX_LIGHT_INDICES %u\n"
-				"#define POISSON %u\n"
-				"#define INDIRECT_ENABLED %u\n"
-				"#define IR_MIPMAP_COUNT %u\n",
+	pps.sprintf(DEFINES,
 		m_clusterCounts[0],
 		m_clusterCounts[1],
 		m_clusterCount,
 		m_r->getWidth(),
 		m_r->getHeight(),
-		m_maxLightIds,
 		m_r->getSm().m_poissonEnabled,
 		1,
-		m_r->getIr().getReflectionTextureMipmapCount());
+		m_r->getIr().getReflectionTextureMipmapCount(),
+		MAX_U8);
 
 	ANKI_CHECK(m_r->createShader("shaders/Is.vert.glsl", m_lightVert, &pps[0]));
-	ANKI_CHECK(m_r->createShader("shaders/Is.frag.glsl", m_lightFrag, &pps[0]));
 
-	m_lightProg = getGrManager().newInstance<ShaderProgram>(m_lightVert->getGrShader(), m_lightFrag->getGrShader());
+	for(U i = 0; i < IS_SHADER_PERMOUTATION_COUNT; ++i)
+	{
+		pps.destroy();
+		pps.sprintf(DEFINES,
+			m_clusterCounts[0],
+			m_clusterCounts[1],
+			m_clusterCount,
+			m_r->getWidth(),
+			m_r->getHeight(),
+			m_r->getSm().m_poissonEnabled,
+			1,
+			m_r->getIr().getReflectionTextureMipmapCount(),
+			i);
+
+		ANKI_CHECK(m_r->createShader("shaders/Is.frag.glsl", m_lightFrags[i], &pps[0]));
+
+		m_lightProgs[i] =
+			getGrManager().newInstance<ShaderProgram>(m_lightVert->getGrShader(), m_lightFrags[i]->getGrShader());
+	}
 
 	//
 	// Create framebuffer
@@ -135,6 +154,14 @@ Error Is::initInternal(const ConfigSet& config)
 	fbInit.m_colorAttachmentCount = 1;
 	fbInit.m_colorAttachments[0].m_texture = m_rt;
 	fbInit.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
+	// fbInit.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::CLEAR;
+	// fbInit.m_colorAttachments[0].m_clearValue.m_colorf = {{1.0, 0.0, 1.0, 0.0}};
+	// TODO
+
+	fbInit.m_depthStencilAttachment.m_texture = m_r->getMs().m_depthRt;
+	fbInit.m_depthStencilAttachment.m_stencilLoadOperation = AttachmentLoadOperation::LOAD;
+	fbInit.m_depthStencilAttachment.m_stencilStoreOperation = AttachmentStoreOperation::DONT_CARE;
+	fbInit.m_depthStencilAttachment.m_aspect = DepthStencilAspectBit::STENCIL;
 	m_fb = getGrManager().newInstance<Framebuffer>(fbInit);
 
 	return ErrorCode::NONE;
@@ -170,7 +197,10 @@ void Is::run(RenderingContext& ctx)
 
 	cmdb->beginRenderPass(m_fb);
 	cmdb->setViewport(0, 0, m_r->getWidth(), m_r->getHeight());
-	cmdb->bindShaderProgram(m_lightProg);
+
+	cmdb->setStencilWriteMask(FaceSelectionBit::FRONT_AND_BACK, 0);
+	cmdb->setStencilCompareMask(FaceSelectionBit::FRONT_AND_BACK, 0xF);
+	cmdb->setStencilCompareOperation(FaceSelectionBit::FRONT_AND_BACK, CompareOperation::EQUAL);
 
 	cmdb->bindTexture(0, 0, m_r->getMs().m_rt0);
 	cmdb->bindTexture(0, 1, m_r->getMs().m_rt1);
@@ -197,8 +227,19 @@ void Is::run(RenderingContext& ctx)
 	bindStorage(cmdb, 0, 0, ctx.m_is.m_clustersToken);
 	bindStorage(cmdb, 0, 1, ctx.m_is.m_lightIndicesToken);
 
-	cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 3);
+	// Draw all permutations
+	for(U i = 0; i < IS_SHADER_PERMOUTATION_COUNT; ++i)
+	{
+		cmdb->bindShaderProgram(m_lightProgs[i]);
+		cmdb->setStencilReference(FaceSelectionBit::FRONT_AND_BACK, i);
+
+		cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 3);
+	}
+
 	cmdb->endRenderPass();
+
+	// Restore state
+	cmdb->setStencilCompareOperation(FaceSelectionBit::FRONT_AND_BACK, CompareOperation::ALWAYS);
 }
 
 void Is::updateCommonBlock(RenderingContext& ctx)

+ 2 - 2
src/anki/renderer/Is.h

@@ -57,8 +57,8 @@ private:
 
 	// Light shaders
 	ShaderResourcePtr m_lightVert;
-	ShaderResourcePtr m_lightFrag;
-	ShaderProgramPtr m_lightProg;
+	Array<ShaderResourcePtr, IS_SHADER_PERMOUTATION_COUNT> m_lightFrags;
+	Array<ShaderProgramPtr, IS_SHADER_PERMOUTATION_COUNT> m_lightProgs;
 
 	LightBin* m_lightBin = nullptr;
 

+ 263 - 20
src/anki/renderer/Ms.cpp

@@ -11,6 +11,8 @@
 #include <anki/scene/FrustumComponent.h>
 #include <anki/misc/ConfigSet.h>
 #include <anki/core/Trace.h>
+#include <anki/scene/LightComponent.h>
+#include <anki/scene/MoveComponent.h>
 
 namespace anki
 {
@@ -19,7 +21,20 @@ Ms::~Ms()
 {
 }
 
-Error Ms::createRt()
+Error Ms::init(const ConfigSet& initializer)
+{
+	ANKI_R_LOGI("Initializing g-buffer pass");
+
+	Error err = initInternal(initializer);
+	if(err)
+	{
+		ANKI_R_LOGE("Failed to initialize g-buffer pass");
+	}
+
+	return err;
+}
+
+Error Ms::initInternal(const ConfigSet& initializer)
 {
 	m_depthRt = m_r->createAndClearRenderTarget(m_r->create2DRenderTargetInitInfo(m_r->getWidth(),
 		m_r->getHeight(),
@@ -74,29 +89,17 @@ Error Ms::createRt()
 	fbInit.m_depthStencilAttachment.m_texture = m_depthRt;
 	fbInit.m_depthStencilAttachment.m_loadOperation = AttachmentLoadOperation::CLEAR;
 	fbInit.m_depthStencilAttachment.m_clearValue.m_depthStencil.m_depth = 1.0;
-	fbInit.m_depthStencilAttachment.m_aspect = DepthStencilAspectBit::DEPTH;
+	fbInit.m_depthStencilAttachment.m_aspect = DepthStencilAspectBit::DEPTH_STENCIL;
 
 	m_fb = getGrManager().newInstance<Framebuffer>(fbInit);
 
-	return ErrorCode::NONE;
-}
+	ANKI_CHECK(m_r->getResourceManager().loadResource(
+		"shaders/MsStencilBufferIsPermutations.vert.glsl", m_isPermutationsVert));
+	ANKI_CHECK(m_r->getResourceManager().loadResource(
+		"shaders/MsStencilBufferIsPermutations.frag.glsl", m_isPermutationsFrag));
+	m_isPermutationsProg = m_r->getGrManager().newInstance<ShaderProgram>(
+		m_isPermutationsVert->getGrShader(), m_isPermutationsFrag->getGrShader());
 
-Error Ms::init(const ConfigSet& initializer)
-{
-	ANKI_R_LOGI("Initializing g-buffer pass");
-
-	Error err = initInternal(initializer);
-	if(err)
-	{
-		ANKI_R_LOGE("Failed to initialize g-buffer pass");
-	}
-
-	return err;
-}
-
-Error Ms::initInternal(const ConfigSet& initializer)
-{
-	ANKI_CHECK(createRt());
 	return ErrorCode::NONE;
 }
 
@@ -141,6 +144,12 @@ Error Ms::buildCommandBuffers(RenderingContext& ctx, U threadId, U threadCount)
 			cmdb,
 			vis.getBegin(VisibilityGroupType::RENDERABLES_MS) + start,
 			vis.getBegin(VisibilityGroupType::RENDERABLES_MS) + end));
+
+		// If last fill stencil buffer
+		if(end == problemSize)
+		{
+			fillStencilBufferWithIsPermutations(ctx, cmdb);
+		}
 	}
 
 	ANKI_TRACE_STOP_EVENT(RENDER_MS);
@@ -208,4 +217,238 @@ void Ms::setPostRunBarriers(RenderingContext& ctx)
 	ANKI_TRACE_STOP_EVENT(RENDER_MS);
 }
 
+void Ms::fillStencilBufferWithIsPermutations(RenderingContext& ctx, CommandBufferPtr& cmdb) const
+{
+	// Set state
+	for(U i = 0; i < MS_COLOR_ATTACHMENT_COUNT; ++i)
+	{
+		cmdb->setColorChannelWriteMask(i, ColorBit::NONE);
+	}
+
+	cmdb->setDepthWrite(false);
+	cmdb->setDepthCompareOperation(CompareOperation::GREATER);
+	cmdb->setCullMode(FaceSelectionBit::FRONT);
+
+	cmdb->setStencilReference(FaceSelectionBit::FRONT_AND_BACK, 0xF);
+	cmdb->setStencilCompareMask(FaceSelectionBit::FRONT_AND_BACK, 0xF);
+	cmdb->setStencilOperations(
+		FaceSelectionBit::FRONT_AND_BACK, StencilOperation::KEEP, StencilOperation::KEEP, StencilOperation::REPLACE);
+	cmdb->setStencilCompareOperation(FaceSelectionBit::FRONT_AND_BACK, CompareOperation::ALWAYS);
+
+	cmdb->bindShaderProgram(m_isPermutationsProg);
+
+	cmdb->setVertexAttribute(0, 0, PixelFormat(ComponentFormat::R32G32B32, TransformFormat::FLOAT), 0);
+	cmdb->setVertexAttribute(1, 1, PixelFormat(ComponentFormat::R32G32B32A32, TransformFormat::FLOAT), 0);
+	cmdb->setVertexAttribute(2, 2, PixelFormat(ComponentFormat::R32G32B32A32, TransformFormat::FLOAT), 0);
+	cmdb->setVertexAttribute(3, 3, PixelFormat(ComponentFormat::R32G32B32A32, TransformFormat::FLOAT), 0);
+	cmdb->setVertexAttribute(4, 4, PixelFormat(ComponentFormat::R32G32B32A32, TransformFormat::FLOAT), 0);
+
+	// Do point lights
+	const Mat4& viewProjMat = ctx.m_viewProjMatJitter;
+	U plightCount = ctx.m_visResults->getCount(VisibilityGroupType::LIGHTS_POINT);
+	StagingGpuMemoryToken plightMvpsToken;
+	if(plightCount)
+	{
+		// Allocate MVP vert buffer
+		Vec4* mvps = static_cast<Vec4*>(m_r->getStagingGpuMemoryManager().allocateFrame(
+			plightCount * sizeof(Mat4), StagingGpuMemoryType::VERTEX, plightMvpsToken));
+
+		// Fill the MVPs
+		const VisibleNode* it = ctx.m_visResults->getBegin(VisibilityGroupType::LIGHTS_POINT);
+		const VisibleNode* end = ctx.m_visResults->getEnd(VisibilityGroupType::LIGHTS_POINT);
+		while(it != end)
+		{
+			const LightComponent& lightc = it->m_node->getComponent<LightComponent>();
+			const MoveComponent& movec = it->m_node->getComponent<MoveComponent>();
+
+			Mat4 modelMat(movec.getWorldTransform().getOrigin().xyz1(),
+				movec.getWorldTransform().getRotation().getRotationPart(),
+				lightc.getRadius());
+
+			Mat4 mvp = viewProjMat * modelMat;
+
+			mvps[0] = mvp.getColumn(0);
+			mvps[1] = mvp.getColumn(1);
+			mvps[2] = mvp.getColumn(2);
+			mvps[3] = mvp.getColumn(3);
+
+			mvps += 4;
+			++it;
+		}
+
+		// Set state and draw
+		cmdb->setStencilWriteMask(FaceSelectionBit::FRONT_AND_BACK, 1 << U(IsShaderPermutationOption::POINT_LIGHTS));
+
+		cmdb->bindVertexBuffer(0, m_r->getLightVolumePrimitives().m_pointLightPositions, 0, sizeof(F32) * 3);
+		cmdb->bindVertexBuffer(1,
+			plightMvpsToken.m_buffer,
+			plightMvpsToken.m_offset + 0 * sizeof(Vec4),
+			sizeof(Mat4),
+			VertexStepRate::INSTANCE);
+		cmdb->bindVertexBuffer(2,
+			plightMvpsToken.m_buffer,
+			plightMvpsToken.m_offset + 1 * sizeof(Vec4),
+			sizeof(Mat4),
+			VertexStepRate::INSTANCE);
+		cmdb->bindVertexBuffer(3,
+			plightMvpsToken.m_buffer,
+			plightMvpsToken.m_offset + 2 * sizeof(Vec4),
+			sizeof(Mat4),
+			VertexStepRate::INSTANCE);
+		cmdb->bindVertexBuffer(4,
+			plightMvpsToken.m_buffer,
+			plightMvpsToken.m_offset + 3 * sizeof(Vec4),
+			sizeof(Mat4),
+			VertexStepRate::INSTANCE);
+		cmdb->bindIndexBuffer(m_r->getLightVolumePrimitives().m_pointLightIndices, 0, IndexType::U16);
+
+		cmdb->drawElements(
+			PrimitiveTopology::TRIANGLES, m_r->getLightVolumePrimitives().m_pointLightIndexCount, plightCount);
+	}
+
+	// Do spot lights
+	U slightCount = ctx.m_visResults->getCount(VisibilityGroupType::LIGHTS_SPOT);
+	StagingGpuMemoryToken slightMvpsToken;
+	if(slightCount)
+	{
+		// Allocate MVP vert buffer
+		Vec4* mvps = static_cast<Vec4*>(m_r->getStagingGpuMemoryManager().allocateFrame(
+			slightCount * sizeof(Mat4), StagingGpuMemoryType::VERTEX, slightMvpsToken));
+
+		// Fill the MVPs
+		const VisibleNode* it = ctx.m_visResults->getBegin(VisibilityGroupType::LIGHTS_SPOT);
+		const VisibleNode* end = ctx.m_visResults->getEnd(VisibilityGroupType::LIGHTS_SPOT);
+		while(it != end)
+		{
+			const LightComponent& lightc = it->m_node->getComponent<LightComponent>();
+			const MoveComponent& movec = it->m_node->getComponent<MoveComponent>();
+
+			Mat4 modelMat(movec.getWorldTransform().getOrigin().xyz1(),
+				movec.getWorldTransform().getRotation().getRotationPart(),
+				1.0);
+
+			// Calc the scale of the cone
+			Mat4 scaleMat(Mat4::getIdentity());
+			scaleMat(0, 0) = tan(lightc.getOuterAngle() / 2.0f) * lightc.getDistance();
+			scaleMat(1, 1) = scaleMat(0, 0);
+			scaleMat(2, 2) = lightc.getDistance();
+
+			modelMat = modelMat * scaleMat;
+
+			Mat4 mvp = viewProjMat * modelMat;
+
+			mvps[0] = mvp.getColumn(0);
+			mvps[1] = mvp.getColumn(1);
+			mvps[2] = mvp.getColumn(2);
+			mvps[3] = mvp.getColumn(3);
+
+			mvps += 4;
+			++it;
+		}
+
+		// Set state and draw
+		cmdb->setStencilWriteMask(FaceSelectionBit::FRONT_AND_BACK, 1 << U(IsShaderPermutationOption::SPOT_LIGHTS));
+
+		cmdb->bindVertexBuffer(0, m_r->getLightVolumePrimitives().m_spotLightPositions, 0, sizeof(F32) * 3);
+		cmdb->bindVertexBuffer(1,
+			slightMvpsToken.m_buffer,
+			slightMvpsToken.m_offset + 0 * sizeof(Vec4),
+			sizeof(Mat4),
+			VertexStepRate::INSTANCE);
+		cmdb->bindVertexBuffer(2,
+			slightMvpsToken.m_buffer,
+			slightMvpsToken.m_offset + 1 * sizeof(Vec4),
+			sizeof(Mat4),
+			VertexStepRate::INSTANCE);
+		cmdb->bindVertexBuffer(3,
+			slightMvpsToken.m_buffer,
+			slightMvpsToken.m_offset + 2 * sizeof(Vec4),
+			sizeof(Mat4),
+			VertexStepRate::INSTANCE);
+		cmdb->bindVertexBuffer(4,
+			slightMvpsToken.m_buffer,
+			slightMvpsToken.m_offset + 3 * sizeof(Vec4),
+			sizeof(Mat4),
+			VertexStepRate::INSTANCE);
+		cmdb->bindIndexBuffer(m_r->getLightVolumePrimitives().m_spotLightIndices, 0, IndexType::U16);
+
+		cmdb->drawElements(
+			PrimitiveTopology::TRIANGLES, m_r->getLightVolumePrimitives().m_spotLightIndexCount, slightCount);
+	}
+
+	// Draw again to remove the areas where the stencil buffer has over exposure
+	cmdb->setDepthCompareOperation(CompareOperation::LESS);
+	cmdb->setCullMode(FaceSelectionBit::BACK);
+
+#if 0
+	cmdb->setStencilOperations(
+		FaceSelectionBit::FRONT_AND_BACK, StencilOperation::KEEP, StencilOperation::ZERO, StencilOperation::KEEP);
+	
+	if(plightCount)
+	{
+		cmdb->setStencilWriteMask(FaceSelectionBit::FRONT_AND_BACK, 1 << U(IsShaderPermutationOption::POINT_LIGHTS));
+	
+		cmdb->bindVertexBuffer(0, m_r->getLightVolumePrimitives().m_pointLightPositions, 0, sizeof(F32) * 3);
+		cmdb->bindVertexBuffer(1,
+			plightMvpsToken.m_buffer,
+			plightMvpsToken.m_offset + 0 * sizeof(Vec4),
+			sizeof(Vec4),
+			VertexStepRate::INSTANCE);
+		cmdb->bindVertexBuffer(2,
+			plightMvpsToken.m_buffer,
+			plightMvpsToken.m_offset + 1 * sizeof(Vec4),
+			sizeof(Vec4),
+			VertexStepRate::INSTANCE);
+		cmdb->bindVertexBuffer(3,
+			plightMvpsToken.m_buffer,
+			plightMvpsToken.m_offset + 2 * sizeof(Vec4),
+			sizeof(Vec4),
+			VertexStepRate::INSTANCE);
+		cmdb->bindVertexBuffer(4,
+			plightMvpsToken.m_buffer, 
+			plightMvpsToken.m_offset + 3 * sizeof(Vec4),
+			sizeof(Vec4),
+			VertexStepRate::INSTANCE);
+		cmdb->bindIndexBuffer(m_r->getLightVolumePrimitives().m_pointLightIndices, 0, IndexType::U16);
+
+		cmdb->drawElements(
+			PrimitiveTopology::TRIANGLES, m_r->getLightVolumePrimitives().m_pointLightIndexCount, plightCount);
+	}
+	
+	if(slightCount)
+	{
+		cmdb->setStencilWriteMask(FaceSelectionBit::FRONT_AND_BACK, 1 << U(IsShaderPermutationOption::SPOT_LIGHTS));
+
+		cmdb->bindVertexBuffer(0, m_r->getLightVolumePrimitives().m_spotLightPositions, 0, sizeof(F32) * 3);
+		cmdb->bindVertexBuffer(1,
+			slightMvpsToken.m_buffer,
+			slightMvpsToken.m_offset + 0 * sizeof(Vec4),
+			sizeof(Vec4),
+			VertexStepRate::INSTANCE);
+		cmdb->bindVertexBuffer(2,
+			slightMvpsToken.m_buffer,
+			slightMvpsToken.m_offset + 1 * sizeof(Vec4),
+			sizeof(Vec4),
+			VertexStepRate::INSTANCE);
+		cmdb->bindVertexBuffer(3,
+			slightMvpsToken.m_buffer,
+			slightMvpsToken.m_offset + 2 * sizeof(Vec4),
+			sizeof(Vec4),
+			VertexStepRate::INSTANCE);
+		cmdb->bindVertexBuffer(4,
+			slightMvpsToken.m_buffer,
+			slightMvpsToken.m_offset + 3 * sizeof(Vec4),
+			sizeof(Vec4),
+			VertexStepRate::INSTANCE);
+		cmdb->bindIndexBuffer(m_r->getLightVolumePrimitives().m_spotLightIndices, 0, IndexType::U16);
+
+		cmdb->drawElements(
+			PrimitiveTopology::TRIANGLES, m_r->getLightVolumePrimitives().m_spotLightIndexCount, slightCount);
+	}
+#endif
+
+	// Restore state
+	cmdb->setStencilWriteMask(FaceSelectionBit::FRONT_AND_BACK, 0);
+}
+
 } // end namespace anki

+ 5 - 2
src/anki/renderer/Ms.h

@@ -43,10 +43,13 @@ anki_internal:
 private:
 	FramebufferPtr m_fb;
 
+	ShaderResourcePtr m_isPermutationsVert;
+	ShaderResourcePtr m_isPermutationsFrag;
+	ShaderProgramPtr m_isPermutationsProg;
+
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& initializer);
 
-	/// Create a G buffer FBO
-	ANKI_USE_RESULT Error createRt();
+	void fillStencilBufferWithIsPermutations(RenderingContext& ctx, CommandBufferPtr& cmdb) const;
 };
 /// @}
 

+ 57 - 0
src/anki/renderer/Renderer.cpp

@@ -26,6 +26,7 @@
 #include <anki/renderer/DepthDownscale.h>
 #include <anki/renderer/Taa.h>
 
+#include <anki/resource/MeshLoader.h>
 #include <cstdarg> // For var args
 
 namespace anki
@@ -41,6 +42,60 @@ static Bool threadWillDoWork(
 	return start != end;
 }
 
+Error LightVolumePrimitives::init(Renderer& r)
+{
+	ANKI_CHECK(
+		loadMesh("engine_data/Plight.ankimesh", r, m_pointLightPositions, m_pointLightIndices, m_pointLightIndexCount));
+	ANKI_CHECK(
+		loadMesh("engine_data/Slight.ankimesh", r, m_spotLightPositions, m_spotLightIndices, m_spotLightIndexCount));
+	return ErrorCode::NONE;
+}
+
+Error LightVolumePrimitives::loadMesh(CString fname, Renderer& r, BufferPtr& vert, BufferPtr& idx, U32& idxCount)
+{
+	MeshLoader loader(&r.getResourceManager());
+	ANKI_CHECK(loader.load(fname));
+
+	PtrSize vertBuffSize = loader.getHeader().m_totalVerticesCount * sizeof(Vec3);
+	vert = r.getGrManager().newInstance<Buffer>(
+		vertBuffSize, BufferUsageBit::VERTEX | BufferUsageBit::BUFFER_UPLOAD_DESTINATION, BufferMapAccessBit::NONE);
+
+	idx = r.getGrManager().newInstance<Buffer>(loader.getIndexDataSize(),
+		BufferUsageBit::INDEX | BufferUsageBit::BUFFER_UPLOAD_DESTINATION,
+		BufferMapAccessBit::NONE);
+
+	// Upload data
+	CommandBufferInitInfo init;
+	init.m_flags = CommandBufferFlag::SMALL_BATCH;
+	CommandBufferPtr cmdb = r.getGrManager().newInstance<CommandBuffer>(init);
+
+	StagingGpuMemoryToken token;
+	Vec3* verts = static_cast<Vec3*>(
+		r.getStagingGpuMemoryManager().allocateFrame(vertBuffSize, StagingGpuMemoryType::TRANSFER, 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->copyBufferToBuffer(token.m_buffer, token.m_offset, vert, 0, token.m_range);
+
+	void* cpuIds =
+		r.getStagingGpuMemoryManager().allocateFrame(loader.getIndexDataSize(), StagingGpuMemoryType::TRANSFER, token);
+
+	memcpy(cpuIds, loader.getIndexData(), loader.getIndexDataSize());
+
+	cmdb->copyBufferToBuffer(token.m_buffer, token.m_offset, idx, 0, token.m_range);
+	idxCount = loader.getHeader().m_totalIndicesCount;
+
+	cmdb->flush();
+
+	return ErrorCode::NONE;
+}
+
 Renderer::Renderer()
 	: m_sceneDrawer(this)
 {
@@ -172,6 +227,8 @@ Error Renderer::initInternal(const ConfigSet& config)
 
 	initJitteredMats();
 
+	ANKI_CHECK(m_lightPrimitives.init(*this));
+
 	return ErrorCode::NONE;
 }
 

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

@@ -29,6 +29,24 @@ class VisibilityTestResults;
 /// @addtogroup renderer
 /// @{
 
+/// Contains shapes of light volumes.
+class LightVolumePrimitives
+{
+public:
+	BufferPtr m_pointLightPositions;
+	BufferPtr m_pointLightIndices;
+	U32 m_pointLightIndexCount;
+
+	BufferPtr m_spotLightPositions;
+	BufferPtr m_spotLightIndices;
+	U32 m_spotLightIndexCount;
+
+	ANKI_USE_RESULT Error init(Renderer& r);
+
+private:
+	ANKI_USE_RESULT Error loadMesh(CString fname, Renderer& r, BufferPtr& vert, BufferPtr& idx, U32& idxCount);
+};
+
 /// Rendering context.
 class RenderingContext
 {
@@ -404,6 +422,11 @@ anki_internal:
 		return m_linearSampler;
 	}
 
+	const LightVolumePrimitives& getLightVolumePrimitives() const
+	{
+		return m_lightPrimitives;
+	}
+
 private:
 	ThreadPool* m_threadpool = nullptr;
 	ResourceManager* m_resources = nullptr;
@@ -463,6 +486,8 @@ private:
 	SamplerPtr m_nearestSampler;
 	SamplerPtr m_linearSampler;
 
+	LightVolumePrimitives m_lightPrimitives;
+
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& initializer);
 
 	ANKI_USE_RESULT Error buildCommandBuffers(RenderingContext& ctx);