Quellcode durchsuchen

Adding stencil support to the GL backend

Panagiotis Christopoulos Charitos vor 9 Jahren
Ursprung
Commit
f1f0af7b88

+ 66 - 13
src/anki/gr/CommandBuffer.h

@@ -140,6 +140,15 @@ public:
 	/// Set depth offset and units.
 	void setPolygonOffset(F32 factor, F32 units);
 
+	/// Set the stencil compare mask.
+	void setStencilCompareMask(FaceSelectionMask face, U32 mask);
+
+	/// Set the stencil write mask.
+	void setStencilWriteMask(FaceSelectionMask face, U32 mask);
+
+	/// Set the stencil reference.
+	void setStencilReference(FaceSelectionMask face, U32 ref);
+
 	/// Bind pipeline.
 	void bindPipeline(PipelinePtr ppline);
 
@@ -166,22 +175,44 @@ public:
 	void dispatchCompute(U32 groupCountX, U32 groupCountY, U32 groupCountZ);
 
 	/// Generate mipmaps for non-3D textures.
-	void generateMipmaps2d(TexturePtr tex, U face, U layer);
+	/// @param tex The texture to generate mips.
+	/// @param face The face of a cube texture or zero.
+	/// @param layer The layer of an array texture or zero.
+	/// @param aspect The aspect of the depth stencil texture. Relevant only for depth stencil textures.
+	void generateMipmaps2d(
+		TexturePtr tex, U face, U layer, DepthStencilAspectMask aspect = DepthStencilAspectMask::NONE);
 
 	/// Generate mipmaps only for 3D textures.
-	void generateMipmaps3d(TexturePtr tex);
+	/// @param tex The texture to generate mips.
+	/// @param aspect The aspect of the depth stencil texture. Relevant only for depth stencil textures.
+	void generateMipmaps3d(TexturePtr tex, DepthStencilAspectMask aspect = DepthStencilAspectMask::NONE);
 
+	// TODO Rename to blit
 	void copyTextureSurfaceToTextureSurface(
 		TexturePtr src, const TextureSurfaceInfo& srcSurf, TexturePtr dest, const TextureSurfaceInfo& destSurf);
 
 	void copyTextureVolumeToTextureVolume(
 		TexturePtr src, const TextureVolumeInfo& srcVol, TexturePtr dest, const TextureVolumeInfo& destVol);
 
-	void clearTexture(TexturePtr tex, const ClearValue& clearValue);
-
-	void clearTextureSurface(TexturePtr tex, const TextureSurfaceInfo& surf, const ClearValue& clearValue);
-
-	void clearTextureVolume(TexturePtr tex, const TextureVolumeInfo& vol, const ClearValue& clearValue);
+	/// Clear a single texture surface. Can be used for all textures except 3D.
+	/// @param tex The texture to clear.
+	/// @param surf The surface to clear.
+	/// @param clearValue The value to clear it with.
+	/// @param aspect The aspect of the depth stencil texture. Relevant only for depth stencil textures.
+	void clearTextureSurface(TexturePtr tex,
+		const TextureSurfaceInfo& surf,
+		const ClearValue& clearValue,
+		DepthStencilAspectMask aspect = DepthStencilAspectMask::NONE);
+
+	/// Clear a volume out of a 3D texture.
+	/// @param tex The texture to clear.
+	/// @param vol The volume to clear.
+	/// @param clearValue The value to clear it with.
+	/// @param aspect The aspect of the depth stencil texture. Relevant only for depth stencil textures.
+	void clearTextureVolume(TexturePtr tex,
+		const TextureVolumeInfo& vol,
+		const ClearValue& clearValue,
+		DepthStencilAspectMask aspect = DepthStencilAspectMask::NONE);
 
 	/// Fill a buffer with some value.
 	/// @param[in,out] buff The buffer to fill.
@@ -201,25 +232,47 @@ public:
 	/// @{
 
 	/// Upload data to a texture surface. It's the base of all texture surface upload methods.
-	void uploadTextureSurface(TexturePtr tex, const TextureSurfaceInfo& surf, const TransientMemoryToken& token);
+	void uploadTextureSurface(TexturePtr tex,
+		const TextureSurfaceInfo& surf,
+		const TransientMemoryToken& token,
+		DepthStencilAspectMask aspect = DepthStencilAspectMask::NONE);
 
 	/// Same as uploadTextureSurface but it will perform the transient allocation as well. If that allocation fails
 	/// expect the defaul OOM behaviour (crash).
-	void uploadTextureSurfaceData(TexturePtr tex, const TextureSurfaceInfo& surf, void*& data, PtrSize& dataSize);
+	void uploadTextureSurfaceData(TexturePtr tex,
+		const TextureSurfaceInfo& surf,
+		void*& data,
+		PtrSize& dataSize,
+		DepthStencilAspectMask aspect = DepthStencilAspectMask::NONE);
 
 	/// Same as uploadTextureSurfaceData but it will return a nullptr in @a data if there is a OOM condition.
-	void tryUploadTextureSurfaceData(TexturePtr tex, const TextureSurfaceInfo& surf, void*& data, PtrSize& dataSize);
+	void tryUploadTextureSurfaceData(TexturePtr tex,
+		const TextureSurfaceInfo& surf,
+		void*& data,
+		PtrSize& dataSize,
+		DepthStencilAspectMask aspect = DepthStencilAspectMask::NONE);
 
 	/// Same as uploadTextureSurface but it will perform the transient allocation and copy to it the @a data. If that
 	/// allocation fails expect the defaul OOM behaviour (crash).
-	void uploadTextureSurfaceCopyData(TexturePtr tex, const TextureSurfaceInfo& surf, void* data, PtrSize dataSize);
+	void uploadTextureSurfaceCopyData(TexturePtr tex,
+		const TextureSurfaceInfo& surf,
+		void* data,
+		PtrSize dataSize,
+		DepthStencilAspectMask aspect = DepthStencilAspectMask::NONE);
 
 	/// Upload data to a texture volume. It's the base of all texture volume upload methods.
-	void uploadTextureVolume(TexturePtr tex, const TextureVolumeInfo& vol, const TransientMemoryToken& token);
+	void uploadTextureVolume(TexturePtr tex,
+		const TextureVolumeInfo& vol,
+		const TransientMemoryToken& token,
+		DepthStencilAspectMask aspect = DepthStencilAspectMask::NONE);
 
 	/// Same as uploadTextureVolume but it will perform the transient allocation and copy to it the @a data. If that
 	/// allocation fails expect the defaul OOM behaviour (crash).
-	void uploadTextureVolumeCopyData(TexturePtr tex, const TextureVolumeInfo& surf, void* data, PtrSize dataSize);
+	void uploadTextureVolumeCopyData(TexturePtr tex,
+		const TextureVolumeInfo& surf,
+		void* data,
+		PtrSize dataSize,
+		DepthStencilAspectMask aspect = DepthStencilAspectMask::NONE);
 
 	/// Upload data to a buffer.
 	void uploadBuffer(BufferPtr buff, PtrSize offset, const TransientMemoryToken& token);

+ 8 - 8
src/anki/gr/CommandBuffer.inl.h

@@ -10,7 +10,7 @@ namespace anki
 {
 
 inline void CommandBuffer::uploadTextureSurfaceData(
-	TexturePtr tex, const TextureSurfaceInfo& surf, void*& data, PtrSize& dataSize)
+	TexturePtr tex, const TextureSurfaceInfo& surf, void*& data, PtrSize& dataSize, DepthStencilAspectMask aspect)
 {
 	PtrSize allocationSize;
 	const BufferUsageBit usage = BufferUsageBit::TEXTURE_UPLOAD_SOURCE;
@@ -20,11 +20,11 @@ inline void CommandBuffer::uploadTextureSurfaceData(
 	TransientMemoryToken token;
 	data = getManager().allocateFrameTransientMemory(allocationSize, usage, token);
 
-	uploadTextureSurface(tex, surf, token);
+	uploadTextureSurface(tex, surf, token, aspect);
 }
 
 inline void CommandBuffer::tryUploadTextureSurfaceData(
-	TexturePtr tex, const TextureSurfaceInfo& surf, void*& data, PtrSize& dataSize)
+	TexturePtr tex, const TextureSurfaceInfo& surf, void*& data, PtrSize& dataSize, DepthStencilAspectMask aspect)
 {
 	PtrSize allocationSize;
 	const BufferUsageBit usage = BufferUsageBit::TEXTURE_UPLOAD_SOURCE;
@@ -36,12 +36,12 @@ inline void CommandBuffer::tryUploadTextureSurfaceData(
 
 	if(data)
 	{
-		uploadTextureSurface(tex, surf, token);
+		uploadTextureSurface(tex, surf, token, aspect);
 	}
 }
 
 inline void CommandBuffer::uploadTextureSurfaceCopyData(
-	TexturePtr tex, const TextureSurfaceInfo& surf, void* data, PtrSize dataSize)
+	TexturePtr tex, const TextureSurfaceInfo& surf, void* data, PtrSize dataSize, DepthStencilAspectMask aspect)
 {
 	PtrSize allocationSize;
 	const BufferUsageBit usage = BufferUsageBit::TEXTURE_UPLOAD_SOURCE;
@@ -52,11 +52,11 @@ inline void CommandBuffer::uploadTextureSurfaceCopyData(
 	void* ptr = getManager().allocateFrameTransientMemory(allocationSize, usage, token);
 	memcpy(ptr, data, dataSize);
 
-	uploadTextureSurface(tex, surf, token);
+	uploadTextureSurface(tex, surf, token, aspect);
 }
 
 inline void CommandBuffer::uploadTextureVolumeCopyData(
-	TexturePtr tex, const TextureVolumeInfo& vol, void* data, PtrSize dataSize)
+	TexturePtr tex, const TextureVolumeInfo& vol, void* data, PtrSize dataSize, DepthStencilAspectMask aspect)
 {
 	PtrSize allocationSize;
 	const BufferUsageBit usage = BufferUsageBit::TEXTURE_UPLOAD_SOURCE;
@@ -67,7 +67,7 @@ inline void CommandBuffer::uploadTextureVolumeCopyData(
 	void* ptr = getManager().allocateFrameTransientMemory(allocationSize, usage, token);
 	memcpy(ptr, data, dataSize);
 
-	uploadTextureVolume(tex, vol, token);
+	uploadTextureVolume(tex, vol, token, aspect);
 }
 
 } // end namespace anki

+ 16 - 0
src/anki/gr/Common.h

@@ -100,6 +100,22 @@ public:
 		Array<U32, 4> m_coloru;
 		Ds m_depthStencil;
 	};
+
+	ClearValue()
+	{
+		memset(this, 0, sizeof(*this));
+	}
+
+	ClearValue(const ClearValue& b)
+	{
+		operator=(b);
+	}
+
+	ClearValue& operator=(const ClearValue& b)
+	{
+		memcpy(this, &b, sizeof(*this));
+		return *this;
+	}
 };
 
 /// A way to identify a surface in a texture.

+ 27 - 4
src/anki/gr/Enums.h

@@ -42,12 +42,13 @@ enum class FillMode : U8
 	SOLID
 };
 
-enum class CullMode : U8
+enum class FaceSelectionMask : U8
 {
-	FRONT,
-	BACK,
-	FRONT_AND_BACK
+	FRONT = 1 << 0,
+	BACK = 1 << 1,
+	FRONT_AND_BACK = FRONT | BACK
 };
+ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(FaceSelectionMask, inline)
 
 enum class CompareOperation : U8
 {
@@ -61,6 +62,18 @@ enum class CompareOperation : U8
 	NEVER
 };
 
+enum class StencilOperation : U8
+{
+	KEEP,
+	ZERO,
+	REPLACE,
+	INCREMENT_AND_CLAMP,
+	DECREMENT_AND_CLAMP,
+	INVERT,
+	INCREMENT_AND_WRAP,
+	DECREMENT_AND_WRAP,
+};
+
 enum class BlendMethod : U8
 {
 	ZERO,
@@ -385,6 +398,16 @@ enum class BufferMapAccessBit : U8
 	WRITE = 1 << 1
 };
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(BufferMapAccessBit, inline)
+
+/// A way to distinguish the aspect of a depth stencil texture.
+enum class DepthStencilAspectMask : U8
+{
+	NONE = 0,
+	DEPTH = 1 << 0,
+	STENCIL = 1 << 1,
+	DEPTH_STENCIL = DEPTH | STENCIL
+};
+ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(DepthStencilAspectMask, inline)
 /// @}
 
 } // end namespace anki

+ 4 - 17
src/anki/gr/Framebuffer.h

@@ -24,26 +24,13 @@ public:
 	AttachmentLoadOperation m_loadOperation = AttachmentLoadOperation::CLEAR;
 	AttachmentStoreOperation m_storeOperation = AttachmentStoreOperation::STORE;
 	ClearValue m_clearValue;
-	TextureUsageBit m_usageInsideRenderPass = TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE;
-
-	FramebufferAttachmentInfo() = default;
 
-	FramebufferAttachmentInfo(const FramebufferAttachmentInfo& b)
-	{
-		operator=(b);
-	}
+	AttachmentLoadOperation m_stencilLoadOperation = AttachmentLoadOperation::CLEAR;
+	AttachmentStoreOperation m_stencilStoreOperation = AttachmentStoreOperation::STORE;
 
-	~FramebufferAttachmentInfo() = default;
+	TextureUsageBit m_usageInsideRenderPass = TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE;
 
-	FramebufferAttachmentInfo& operator=(const FramebufferAttachmentInfo& b)
-	{
-		m_texture = b.m_texture;
-		m_surface = b.m_surface;
-		m_loadOperation = b.m_loadOperation;
-		m_storeOperation = b.m_storeOperation;
-		memcpy(&m_clearValue, &b.m_clearValue, sizeof(m_clearValue));
-		return *this;
-	}
+	DepthStencilAspectMask m_aspect = DepthStencilAspectMask::NONE; ///< Relevant only for depth stencil textures.
 };
 
 /// Framebuffer initializer. If you require the default framebuffer then set m_colorAttachmentCount to 1 and don't set a

+ 20 - 1
src/anki/gr/Pipeline.h

@@ -62,14 +62,33 @@ class RasterizerStateInfo
 {
 public:
 	FillMode m_fillMode = FillMode::SOLID;
-	CullMode m_cullMode = CullMode::BACK;
+	FaceSelectionMask m_cullMode = FaceSelectionMask::BACK;
 };
 
+class StencilStateInfo
+{
+public:
+	StencilOperation m_stencilFailOperation = StencilOperation::KEEP;
+	StencilOperation m_stencilPassDepthFailOperation = StencilOperation::KEEP;
+	StencilOperation m_stencilPassDepthPassOperation = StencilOperation::KEEP;
+	CompareOperation m_compareFunction = CompareOperation::ALWAYS;
+};
+
+/// Depth stencil state.
+/// To disable depth test set m_depthWriteEnabled to false and m_depthCompareFunction to always.
+/// To disable stencil test leave m_stencilFront and m_stencilBack as is.
 class DepthStencilStateInfo
 {
 public:
+	// Depth
 	Bool8 m_depthWriteEnabled = true;
 	CompareOperation m_depthCompareFunction = CompareOperation::LESS;
+
+	// Stencil
+	StencilStateInfo m_stencilFront;
+	StencilStateInfo m_stencilBack;
+
+	// Common
 	PixelFormat m_format;
 
 	Bool isInUse() const

+ 1 - 0
src/anki/gr/ResourceGroup.h

@@ -23,6 +23,7 @@ public:
 	TexturePtr m_texture;
 	SamplerPtr m_sampler; ///< Use it to override texture's sampler.
 	TextureUsageBit m_usage = TextureUsageBit::SAMPLED_FRAGMENT;
+	DepthStencilAspectMask m_aspect = DepthStencilAspectMask::NONE; ///< Relevant only for depth stencil textures.
 };
 
 /// Buffer binding info.

+ 9 - 0
src/anki/gr/common/Misc.cpp

@@ -7,6 +7,7 @@
 #include <anki/gr/Framebuffer.h>
 #include <anki/gr/Texture.h>
 #include <anki/util/StringList.h>
+#include <anki/gr/Pipeline.h>
 
 namespace anki
 {
@@ -233,4 +234,12 @@ PtrSize computeVolumeSize(U width, U height, U depth, const PixelFormat& fmt)
 	}
 }
 
+Bool stencilTestDisabled(const StencilStateInfo& inf)
+{
+	return inf.m_stencilFailOperation == StencilOperation::KEEP
+		&& inf.m_stencilPassDepthFailOperation == StencilOperation::KEEP
+		&& inf.m_stencilPassDepthPassOperation == StencilOperation::KEEP
+		&& inf.m_compareFunction == CompareOperation::ALWAYS;
+}
+
 } // end namespace anki

+ 5 - 0
src/anki/gr/common/Misc.h

@@ -10,6 +10,9 @@
 namespace anki
 {
 
+// Forward
+class StencilStateInfo;
+
 enum class TransientBufferType
 {
 	UNIFORM,
@@ -84,4 +87,6 @@ PtrSize computeSurfaceSize(U width, U height, const PixelFormat& fmt);
 /// Compute the size of the volume.
 PtrSize computeVolumeSize(U width, U height, U depth, const PixelFormat& fmt);
 
+Bool stencilTestDisabled(const StencilStateInfo& inf);
+
 } // end namespace anki

+ 382 - 254
src/anki/gr/gl/CommandBuffer.cpp

@@ -79,130 +79,131 @@ void CommandBuffer::finish()
 	getManager().getImplementation().getRenderingThread().finishCommandBuffer(CommandBufferPtr(this));
 }
 
-class ViewportCommand final : public GlCommand
+void CommandBuffer::setViewport(U16 minx, U16 miny, U16 maxx, U16 maxy)
 {
-public:
-	Array<U16, 4> m_value;
-
-	ViewportCommand(U16 a, U16 b, U16 c, U16 d)
+	class ViewportCommand final : public GlCommand
 	{
-		m_value = {{a, b, c, d}};
-	}
+	public:
+		Array<U16, 4> m_value;
 
-	Error operator()(GlState& state)
-	{
-		if(state.m_viewport[0] != m_value[0] || state.m_viewport[1] != m_value[1] || state.m_viewport[2] != m_value[2]
-			|| state.m_viewport[3] != m_value[3])
+		ViewportCommand(U16 a, U16 b, U16 c, U16 d)
 		{
-			glViewport(m_value[0], m_value[1], m_value[2], m_value[3]);
-
-			state.m_viewport = m_value;
+			m_value = {{a, b, c, d}};
 		}
 
-		return ErrorCode::NONE;
-	}
-};
+		Error operator()(GlState& state)
+		{
+			if(state.m_viewport[0] != m_value[0] || state.m_viewport[1] != m_value[1]
+				|| state.m_viewport[2] != m_value[2]
+				|| state.m_viewport[3] != m_value[3])
+			{
+				glViewport(m_value[0], m_value[1], m_value[2], m_value[3]);
+
+				state.m_viewport = m_value;
+			}
+
+			return ErrorCode::NONE;
+		}
+	};
 
-void CommandBuffer::setViewport(U16 minx, U16 miny, U16 maxx, U16 maxy)
-{
 #if ANKI_ASSERTS_ENABLED
 	m_impl->m_dbg.m_viewport = true;
 #endif
 	m_impl->pushBackNewCommand<ViewportCommand>(minx, miny, maxx, maxy);
 }
 
-class SetPolygonOffsetCommand final : public GlCommand
+void CommandBuffer::setPolygonOffset(F32 factor, F32 units)
 {
-public:
-	F32 m_factor;
-	F32 m_units;
-
-	SetPolygonOffsetCommand(F32 factor, F32 units)
-		: m_factor(factor)
-		, m_units(units)
+	class SetPolygonOffsetCommand final : public GlCommand
 	{
-	}
+	public:
+		F32 m_factor;
+		F32 m_units;
 
-	Error operator()(GlState& state)
-	{
-		if(m_factor == 0.0 && m_units == 0.0)
+		SetPolygonOffsetCommand(F32 factor, F32 units)
+			: m_factor(factor)
+			, m_units(units)
 		{
-			glDisable(GL_POLYGON_OFFSET_FILL);
 		}
-		else
+
+		Error operator()(GlState& state)
 		{
-			glEnable(GL_POLYGON_OFFSET_FILL);
-			glPolygonOffset(m_factor, m_units);
-		}
+			if(m_factor == 0.0 && m_units == 0.0)
+			{
+				glDisable(GL_POLYGON_OFFSET_FILL);
+			}
+			else
+			{
+				glEnable(GL_POLYGON_OFFSET_FILL);
+				glPolygonOffset(m_factor, m_units);
+			}
 
-		return ErrorCode::NONE;
-	}
-};
+			return ErrorCode::NONE;
+		}
+	};
 
-void CommandBuffer::setPolygonOffset(F32 factor, F32 units)
-{
 #if ANKI_ASSERTS_ENABLED
 	m_impl->m_dbg.m_polygonOffset = true;
 #endif
 	m_impl->pushBackNewCommand<SetPolygonOffsetCommand>(factor, units);
 }
 
-class BindPipelineCommand final : public GlCommand
+void CommandBuffer::bindPipeline(PipelinePtr ppline)
 {
-public:
-	PipelinePtr m_ppline;
-
-	BindPipelineCommand(PipelinePtr& ppline)
-		: m_ppline(ppline)
+	class BindPipelineCommand final : public GlCommand
 	{
-	}
+	public:
+		PipelinePtr m_ppline;
 
-	Error operator()(GlState& state)
-	{
-		if(state.m_lastPplineBoundUuid != m_ppline->getUuid())
+		BindPipelineCommand(PipelinePtr& ppline)
+			: m_ppline(ppline)
 		{
-			ANKI_TRACE_START_EVENT(GL_BIND_PPLINE);
-
-			PipelineImpl& impl = m_ppline->getImplementation();
-			impl.bind(state);
-			state.m_lastPplineBoundUuid = m_ppline->getUuid();
-			ANKI_TRACE_INC_COUNTER(GR_PIPELINE_BINDS_HAPPENED, 1);
-
-			ANKI_TRACE_STOP_EVENT(GL_BIND_PPLINE);
 		}
-		else
+
+		Error operator()(GlState& state)
 		{
-			ANKI_TRACE_INC_COUNTER(GR_PIPELINE_BINDS_SKIPPED, 1);
-		}
+			if(state.m_lastPplineBoundUuid != m_ppline->getUuid())
+			{
+				ANKI_TRACE_START_EVENT(GL_BIND_PPLINE);
+
+				PipelineImpl& impl = m_ppline->getImplementation();
+				impl.bind(state);
+				state.m_lastPplineBoundUuid = m_ppline->getUuid();
+				ANKI_TRACE_INC_COUNTER(GR_PIPELINE_BINDS_HAPPENED, 1);
+
+				ANKI_TRACE_STOP_EVENT(GL_BIND_PPLINE);
+			}
+			else
+			{
+				ANKI_TRACE_INC_COUNTER(GR_PIPELINE_BINDS_SKIPPED, 1);
+			}
 
-		return ErrorCode::NONE;
-	}
-};
+			return ErrorCode::NONE;
+		}
+	};
 
-void CommandBuffer::bindPipeline(PipelinePtr ppline)
-{
 	m_impl->pushBackNewCommand<BindPipelineCommand>(ppline);
 }
 
-class BindFramebufferCommand final : public GlCommand
+void CommandBuffer::beginRenderPass(FramebufferPtr fb)
 {
-public:
-	FramebufferPtr m_fb;
-
-	BindFramebufferCommand(FramebufferPtr fb)
-		: m_fb(fb)
+	class BindFramebufferCommand final : public GlCommand
 	{
-	}
+	public:
+		FramebufferPtr m_fb;
 
-	Error operator()(GlState& state)
-	{
-		m_fb->getImplementation().bind(state);
-		return ErrorCode::NONE;
-	}
-};
+		BindFramebufferCommand(FramebufferPtr fb)
+			: m_fb(fb)
+		{
+		}
+
+		Error operator()(GlState& state)
+		{
+			m_fb->getImplementation().bind(state);
+			return ErrorCode::NONE;
+		}
+	};
 
-void CommandBuffer::beginRenderPass(FramebufferPtr fb)
-{
 	ANKI_ASSERT(!m_impl->m_dbg.m_insideRenderPass);
 #if ANKI_ASSERTS_ENABLED
 	m_impl->m_dbg.m_insideRenderPass = true;
@@ -254,161 +255,172 @@ void CommandBuffer::resetOcclusionQuery(OcclusionQueryPtr query)
 	// Nothing for GL
 }
 
-class OqBeginCommand final : public GlCommand
+void CommandBuffer::beginOcclusionQuery(OcclusionQueryPtr query)
 {
-public:
-	OcclusionQueryPtr m_handle;
-
-	OqBeginCommand(const OcclusionQueryPtr& handle)
-		: m_handle(handle)
+	class OqBeginCommand final : public GlCommand
 	{
-	}
+	public:
+		OcclusionQueryPtr m_handle;
 
-	Error operator()(GlState&)
-	{
-		m_handle->getImplementation().begin();
-		return ErrorCode::NONE;
-	}
-};
+		OqBeginCommand(const OcclusionQueryPtr& handle)
+			: m_handle(handle)
+		{
+		}
+
+		Error operator()(GlState&)
+		{
+			m_handle->getImplementation().begin();
+			return ErrorCode::NONE;
+		}
+	};
 
-void CommandBuffer::beginOcclusionQuery(OcclusionQueryPtr query)
-{
 	m_impl->pushBackNewCommand<OqBeginCommand>(query);
 }
 
-class OqEndCommand final : public GlCommand
+void CommandBuffer::endOcclusionQuery(OcclusionQueryPtr query)
 {
-public:
-	OcclusionQueryPtr m_handle;
-
-	OqEndCommand(const OcclusionQueryPtr& handle)
-		: m_handle(handle)
+	class OqEndCommand final : public GlCommand
 	{
-	}
+	public:
+		OcclusionQueryPtr m_handle;
 
-	Error operator()(GlState&)
-	{
-		m_handle->getImplementation().end();
-		return ErrorCode::NONE;
-	}
-};
+		OqEndCommand(const OcclusionQueryPtr& handle)
+			: m_handle(handle)
+		{
+		}
+
+		Error operator()(GlState&)
+		{
+			m_handle->getImplementation().end();
+			return ErrorCode::NONE;
+		}
+	};
 
-void CommandBuffer::endOcclusionQuery(OcclusionQueryPtr query)
-{
 	m_impl->pushBackNewCommand<OqEndCommand>(query);
 }
 
-class TexSurfUploadCommand final : public GlCommand
+void CommandBuffer::uploadTextureSurface(
+	TexturePtr tex, const TextureSurfaceInfo& surf, const TransientMemoryToken& token, DepthStencilAspectMask aspect)
 {
-public:
-	TexturePtr m_handle;
-	TextureSurfaceInfo m_surf;
-	TransientMemoryToken m_token;
-
-	TexSurfUploadCommand(const TexturePtr& handle, TextureSurfaceInfo surf, const TransientMemoryToken& token)
-		: m_handle(handle)
-		, m_surf(surf)
-		, m_token(token)
+	class TexSurfUploadCommand final : public GlCommand
 	{
-	}
+	public:
+		TexturePtr m_handle;
+		TextureSurfaceInfo m_surf;
+		TransientMemoryToken m_token;
+		DepthStencilAspectMask m_aspect;
+
+		TexSurfUploadCommand(const TexturePtr& handle,
+			TextureSurfaceInfo surf,
+			const TransientMemoryToken& token,
+			DepthStencilAspectMask aspect)
+			: m_handle(handle)
+			, m_surf(surf)
+			, m_token(token)
+			, m_aspect(aspect)
+		{
+		}
 
-	Error operator()(GlState& state)
-	{
-		void* data = state.m_manager->getImplementation().getTransientMemoryManager().getBaseAddress(m_token);
-		data = static_cast<void*>(static_cast<U8*>(data) + m_token.m_offset);
+		Error operator()(GlState& state)
+		{
+			void* data = state.m_manager->getImplementation().getTransientMemoryManager().getBaseAddress(m_token);
+			data = static_cast<void*>(static_cast<U8*>(data) + m_token.m_offset);
 
-		m_handle->getImplementation().writeSurface(m_surf, data, m_token.m_range);
+			m_handle->getImplementation().writeSurface(m_surf, data, m_token.m_range, m_aspect);
 
-		if(m_token.m_lifetime == TransientMemoryTokenLifetime::PERSISTENT)
-		{
-			state.m_manager->getImplementation().getTransientMemoryManager().free(m_token);
-		}
+			if(m_token.m_lifetime == TransientMemoryTokenLifetime::PERSISTENT)
+			{
+				state.m_manager->getImplementation().getTransientMemoryManager().free(m_token);
+			}
 
-		return ErrorCode::NONE;
-	}
-};
+			return ErrorCode::NONE;
+		}
+	};
 
-void CommandBuffer::uploadTextureSurface(
-	TexturePtr tex, const TextureSurfaceInfo& surf, const TransientMemoryToken& token)
-{
 	ANKI_ASSERT(tex);
 	ANKI_ASSERT(token.m_range > 0);
 	ANKI_ASSERT(!m_impl->m_dbg.m_insideRenderPass);
 
-	m_impl->pushBackNewCommand<TexSurfUploadCommand>(tex, surf, token);
+	m_impl->pushBackNewCommand<TexSurfUploadCommand>(tex, surf, token, aspect);
 }
 
-class TexVolUploadCommand final : public GlCommand
+void CommandBuffer::uploadTextureVolume(
+	TexturePtr tex, const TextureVolumeInfo& vol, const TransientMemoryToken& token, DepthStencilAspectMask aspect)
 {
-public:
-	TexturePtr m_handle;
-	TextureVolumeInfo m_vol;
-	TransientMemoryToken m_token;
-
-	TexVolUploadCommand(const TexturePtr& handle, TextureVolumeInfo vol, const TransientMemoryToken& token)
-		: m_handle(handle)
-		, m_vol(vol)
-		, m_token(token)
+	class TexVolUploadCommand final : public GlCommand
 	{
-	}
+	public:
+		TexturePtr m_handle;
+		TextureVolumeInfo m_vol;
+		TransientMemoryToken m_token;
+		DepthStencilAspectMask m_aspect;
+
+		TexVolUploadCommand(const TexturePtr& handle,
+			TextureVolumeInfo vol,
+			const TransientMemoryToken& token,
+			DepthStencilAspectMask aspect)
+			: m_handle(handle)
+			, m_vol(vol)
+			, m_token(token)
+			, m_aspect(aspect)
+		{
+		}
 
-	Error operator()(GlState& state)
-	{
-		void* data = state.m_manager->getImplementation().getTransientMemoryManager().getBaseAddress(m_token);
-		data = static_cast<void*>(static_cast<U8*>(data) + m_token.m_offset);
+		Error operator()(GlState& state)
+		{
+			void* data = state.m_manager->getImplementation().getTransientMemoryManager().getBaseAddress(m_token);
+			data = static_cast<void*>(static_cast<U8*>(data) + m_token.m_offset);
 
-		m_handle->getImplementation().writeVolume(m_vol, data, m_token.m_range);
+			m_handle->getImplementation().writeVolume(m_vol, data, m_token.m_range, m_aspect);
 
-		if(m_token.m_lifetime == TransientMemoryTokenLifetime::PERSISTENT)
-		{
-			state.m_manager->getImplementation().getTransientMemoryManager().free(m_token);
-		}
+			if(m_token.m_lifetime == TransientMemoryTokenLifetime::PERSISTENT)
+			{
+				state.m_manager->getImplementation().getTransientMemoryManager().free(m_token);
+			}
 
-		return ErrorCode::NONE;
-	}
-};
+			return ErrorCode::NONE;
+		}
+	};
 
-void CommandBuffer::uploadTextureVolume(TexturePtr tex, const TextureVolumeInfo& vol, const TransientMemoryToken& token)
-{
 	ANKI_ASSERT(tex);
 	ANKI_ASSERT(token.m_range > 0);
 	ANKI_ASSERT(!m_impl->m_dbg.m_insideRenderPass);
 
-	m_impl->pushBackNewCommand<TexVolUploadCommand>(tex, vol, token);
+	m_impl->pushBackNewCommand<TexVolUploadCommand>(tex, vol, token, aspect);
 }
 
-class BuffWriteCommand final : public GlCommand
+void CommandBuffer::uploadBuffer(BufferPtr buff, PtrSize offset, const TransientMemoryToken& token)
 {
-public:
-	BufferPtr m_handle;
-	PtrSize m_offset;
-	TransientMemoryToken m_token;
-
-	BuffWriteCommand(const BufferPtr& handle, PtrSize offset, const TransientMemoryToken& token)
-		: m_handle(handle)
-		, m_offset(offset)
-		, m_token(token)
-	{
-	}
-
-	Error operator()(GlState& state)
+	class BuffWriteCommand final : public GlCommand
 	{
-		void* data = state.m_manager->getImplementation().getTransientMemoryManager().getBaseAddress(m_token);
-		data = static_cast<void*>(static_cast<U8*>(data) + m_token.m_offset);
-
-		m_handle->getImplementation().write(data, m_offset, m_token.m_range);
+	public:
+		BufferPtr m_handle;
+		PtrSize m_offset;
+		TransientMemoryToken m_token;
 
-		if(m_token.m_lifetime == TransientMemoryTokenLifetime::PERSISTENT)
+		BuffWriteCommand(const BufferPtr& handle, PtrSize offset, const TransientMemoryToken& token)
+			: m_handle(handle)
+			, m_offset(offset)
+			, m_token(token)
 		{
-			state.m_manager->getImplementation().getTransientMemoryManager().free(m_token);
 		}
 
-		return ErrorCode::NONE;
-	}
-};
+		Error operator()(GlState& state)
+		{
+			void* data = state.m_manager->getImplementation().getTransientMemoryManager().getBaseAddress(m_token);
+			data = static_cast<void*>(static_cast<U8*>(data) + m_token.m_offset);
+
+			m_handle->getImplementation().write(data, m_offset, m_token.m_range);
+
+			if(m_token.m_lifetime == TransientMemoryTokenLifetime::PERSISTENT)
+			{
+				state.m_manager->getImplementation().getTransientMemoryManager().free(m_token);
+			}
+
+			return ErrorCode::NONE;
+		}
+	};
 
-void CommandBuffer::uploadBuffer(BufferPtr buff, PtrSize offset, const TransientMemoryToken& token)
-{
 	ANKI_ASSERT(token.m_range > 0);
 	ANKI_ASSERT(buff);
 	ANKI_ASSERT(!m_impl->m_dbg.m_insideRenderPass);
@@ -416,31 +428,38 @@ void CommandBuffer::uploadBuffer(BufferPtr buff, PtrSize offset, const Transient
 	m_impl->pushBackNewCommand<BuffWriteCommand>(buff, offset, token);
 }
 
-class GenMipsCommand final : public GlCommand
+void CommandBuffer::generateMipmaps2d(TexturePtr tex, U face, U layer, DepthStencilAspectMask aspect)
 {
-public:
-	TexturePtr m_tex;
-	U8 m_face;
-	U32 m_layer;
-
-	GenMipsCommand(const TexturePtr& tex, U face, U layer)
-		: m_tex(tex)
-		, m_face(face)
-		, m_layer(layer)
+	class GenMipsCommand final : public GlCommand
 	{
-	}
+	public:
+		TexturePtr m_tex;
+		U8 m_face;
+		U32 m_layer;
+		DepthStencilAspectMask m_aspect;
 
-	Error operator()(GlState&)
-	{
-		m_tex->getImplementation().generateMipmaps2d(m_face, m_layer);
-		return ErrorCode::NONE;
-	}
-};
+		GenMipsCommand(const TexturePtr& tex, U face, U layer, DepthStencilAspectMask aspect)
+			: m_tex(tex)
+			, m_face(face)
+			, m_layer(layer)
+			, m_aspect(aspect)
+		{
+		}
+
+		Error operator()(GlState&)
+		{
+			m_tex->getImplementation().generateMipmaps2d(m_face, m_layer, m_aspect);
+			return ErrorCode::NONE;
+		}
+	};
 
-void CommandBuffer::generateMipmaps2d(TexturePtr tex, U face, U layer)
-{
 	ANKI_ASSERT(!m_impl->m_dbg.m_insideRenderPass);
-	m_impl->pushBackNewCommand<GenMipsCommand>(tex, face, layer);
+	m_impl->pushBackNewCommand<GenMipsCommand>(tex, face, layer, aspect);
+}
+
+void CommandBuffer::generateMipmaps3d(TexturePtr tex, DepthStencilAspectMask aspect)
+{
+	ANKI_ASSERT(!!"TODO");
 }
 
 CommandBufferInitHints CommandBuffer::computeInitHints() const
@@ -448,27 +467,27 @@ CommandBufferInitHints CommandBuffer::computeInitHints() const
 	return m_impl->computeInitHints();
 }
 
-class ExecCmdbCommand final : public GlCommand
+void CommandBuffer::pushSecondLevelCommandBuffer(CommandBufferPtr cmdb)
 {
-public:
-	CommandBufferPtr m_cmdb;
-
-	ExecCmdbCommand(const CommandBufferPtr& cmdb)
-		: m_cmdb(cmdb)
+	class ExecCmdbCommand final : public GlCommand
 	{
-	}
+	public:
+		CommandBufferPtr m_cmdb;
 
-	Error operator()(GlState&)
-	{
-		ANKI_TRACE_START_EVENT(GL_2ND_LEVEL_CMD_BUFFER);
-		Error err = m_cmdb->getImplementation().executeAllCommands();
-		ANKI_TRACE_STOP_EVENT(GL_2ND_LEVEL_CMD_BUFFER);
-		return err;
-	}
-};
+		ExecCmdbCommand(const CommandBufferPtr& cmdb)
+			: m_cmdb(cmdb)
+		{
+		}
+
+		Error operator()(GlState&)
+		{
+			ANKI_TRACE_START_EVENT(GL_2ND_LEVEL_CMD_BUFFER);
+			Error err = m_cmdb->getImplementation().executeAllCommands();
+			ANKI_TRACE_STOP_EVENT(GL_2ND_LEVEL_CMD_BUFFER);
+			return err;
+		}
+	};
 
-void CommandBuffer::pushSecondLevelCommandBuffer(CommandBufferPtr cmdb)
-{
 	m_impl->pushBackNewCommand<ExecCmdbCommand>(cmdb);
 }
 
@@ -477,33 +496,33 @@ Bool CommandBuffer::isEmpty() const
 	return m_impl->isEmpty();
 }
 
-class CopyTexCommand final : public GlCommand
+void CommandBuffer::copyTextureSurfaceToTextureSurface(
+	TexturePtr src, const TextureSurfaceInfo& srcSurf, TexturePtr dest, const TextureSurfaceInfo& destSurf)
 {
-public:
-	TexturePtr m_src;
-	TextureSurfaceInfo m_srcSurf;
-	TexturePtr m_dest;
-	TextureSurfaceInfo m_destSurf;
-
-	CopyTexCommand(
-		TexturePtr src, const TextureSurfaceInfo& srcSurf, TexturePtr dest, const TextureSurfaceInfo& destSurf)
-		: m_src(src)
-		, m_srcSurf(srcSurf)
-		, m_dest(dest)
-		, m_destSurf(destSurf)
+	class CopyTexCommand final : public GlCommand
 	{
-	}
+	public:
+		TexturePtr m_src;
+		TextureSurfaceInfo m_srcSurf;
+		TexturePtr m_dest;
+		TextureSurfaceInfo m_destSurf;
+
+		CopyTexCommand(
+			TexturePtr src, const TextureSurfaceInfo& srcSurf, TexturePtr dest, const TextureSurfaceInfo& destSurf)
+			: m_src(src)
+			, m_srcSurf(srcSurf)
+			, m_dest(dest)
+			, m_destSurf(destSurf)
+		{
+		}
 
-	Error operator()(GlState&)
-	{
-		TextureImpl::copy(m_src->getImplementation(), m_srcSurf, m_dest->getImplementation(), m_destSurf);
-		return ErrorCode::NONE;
-	}
-};
+		Error operator()(GlState&)
+		{
+			TextureImpl::copy(m_src->getImplementation(), m_srcSurf, m_dest->getImplementation(), m_destSurf);
+			return ErrorCode::NONE;
+		}
+	};
 
-void CommandBuffer::copyTextureSurfaceToTextureSurface(
-	TexturePtr src, const TextureSurfaceInfo& srcSurf, TexturePtr dest, const TextureSurfaceInfo& destSurf)
-{
 	ANKI_ASSERT(!m_impl->m_dbg.m_insideRenderPass);
 	m_impl->pushBackNewCommand<CopyTexCommand>(src, srcSurf, dest, destSurf);
 }
@@ -583,7 +602,8 @@ void CommandBuffer::setTextureVolumeBarrier(
 	// Do nothing
 }
 
-void CommandBuffer::clearTextureSurface(TexturePtr tex, const TextureSurfaceInfo& surf, const ClearValue& clearValue)
+void CommandBuffer::clearTextureSurface(
+	TexturePtr tex, const TextureSurfaceInfo& surf, const ClearValue& clearValue, DepthStencilAspectMask aspect)
 {
 	class ClearTextCommand final : public GlCommand
 	{
@@ -591,22 +611,25 @@ void CommandBuffer::clearTextureSurface(TexturePtr tex, const TextureSurfaceInfo
 		TexturePtr m_tex;
 		ClearValue m_val;
 		TextureSurfaceInfo m_surf;
+		DepthStencilAspectMask m_aspect;
 
-		ClearTextCommand(TexturePtr tex, const TextureSurfaceInfo& surf, const ClearValue& val)
+		ClearTextCommand(
+			TexturePtr tex, const TextureSurfaceInfo& surf, const ClearValue& val, DepthStencilAspectMask aspect)
 			: m_tex(tex)
 			, m_val(val)
 			, m_surf(surf)
+			, m_aspect(aspect)
 		{
 		}
 
 		Error operator()(GlState&)
 		{
-			m_tex->getImplementation().clear(m_surf, m_val);
+			m_tex->getImplementation().clear(m_surf, m_val, m_aspect);
 			return ErrorCode::NONE;
 		}
 	};
 
-	m_impl->pushBackNewCommand<ClearTextCommand>(tex, surf, clearValue);
+	m_impl->pushBackNewCommand<ClearTextCommand>(tex, surf, clearValue, aspect);
 }
 
 void CommandBuffer::fillBuffer(BufferPtr buff, PtrSize offset, PtrSize size, U32 value)
@@ -671,4 +694,109 @@ void CommandBuffer::writeOcclusionQueryResultToBuffer(OcclusionQueryPtr query, P
 	m_impl->pushBackNewCommand<WriteOcclResultToBuff>(query, offset, buff);
 }
 
+void CommandBuffer::setStencilCompareMask(FaceSelectionMask face, U32 mask)
+{
+	class SetStencilCompareMask final : public GlCommand
+	{
+	public:
+		FaceSelectionMask m_face;
+		U32 m_mask;
+
+		SetStencilCompareMask(FaceSelectionMask face, U32 mask)
+			: m_face(face)
+			, m_mask(mask)
+		{
+		}
+
+		Error operator()(GlState& state)
+		{
+			if(!!(m_face & FaceSelectionMask::FRONT) && state.m_stencilCompareMask[0] != m_mask)
+			{
+				state.m_stencilCompareMask[0] = m_mask;
+				state.m_glStencilFuncSeparateDirtyMask |= 1 << 0;
+			}
+
+			if(!!(m_face & FaceSelectionMask::BACK) && state.m_stencilCompareMask[1] != m_mask)
+			{
+				state.m_stencilCompareMask[1] = m_mask;
+				state.m_glStencilFuncSeparateDirtyMask |= 1 << 1;
+			}
+
+			return ErrorCode::NONE;
+		}
+	};
+
+	m_impl->pushBackNewCommand<SetStencilCompareMask>(face, mask);
+}
+
+void CommandBuffer::setStencilWriteMask(FaceSelectionMask face, U32 mask)
+{
+	class SetStencilWriteMask final : public GlCommand
+	{
+	public:
+		FaceSelectionMask m_face;
+		U32 m_mask;
+
+		SetStencilWriteMask(FaceSelectionMask face, U32 mask)
+			: m_face(face)
+			, m_mask(mask)
+		{
+		}
+
+		Error operator()(GlState& state)
+		{
+			if(!!(m_face & FaceSelectionMask::FRONT) && state.m_stencilWriteMask[0] != m_mask)
+			{
+				glStencilMaskSeparate(GL_FRONT, m_mask);
+				state.m_stencilWriteMask[0] = m_mask;
+			}
+
+			if(!!(m_face & FaceSelectionMask::BACK) && state.m_stencilWriteMask[1] != m_mask)
+			{
+				glStencilMaskSeparate(GL_BACK, m_mask);
+				state.m_stencilWriteMask[1] = m_mask;
+			}
+
+			return ErrorCode::NONE;
+		}
+	};
+
+	m_impl->pushBackNewCommand<SetStencilWriteMask>(face, mask);
+}
+
+void CommandBuffer::setStencilReference(FaceSelectionMask face, U32 ref)
+{
+	class SetStencilRefMask final : public GlCommand
+	{
+	public:
+		FaceSelectionMask m_face;
+		U32 m_ref;
+
+		SetStencilRefMask(FaceSelectionMask face, U32 ref)
+			: m_face(face)
+			, m_ref(ref)
+		{
+		}
+
+		Error operator()(GlState& state)
+		{
+			if(!!(m_face & FaceSelectionMask::FRONT) && state.m_stencilRef[0] != m_ref)
+			{
+				state.m_stencilRef[0] = m_ref;
+				state.m_glStencilFuncSeparateDirtyMask |= 1 << 0;
+			}
+
+			if(!!(m_face & FaceSelectionMask::BACK) && state.m_stencilRef[1] != m_ref)
+			{
+				state.m_stencilRef[1] = m_ref;
+				state.m_glStencilFuncSeparateDirtyMask |= 1 << 1;
+			}
+
+			return ErrorCode::NONE;
+		}
+	};
+
+	m_impl->pushBackNewCommand<SetStencilRefMask>(face, ref);
+}
+
 } // end namespace anki

+ 4 - 0
src/anki/gr/gl/CommandBufferImpl.cpp

@@ -158,6 +158,7 @@ void CommandBufferImpl::drawElements(U32 count, U32 instanceCount, U32 firstInde
 			};
 
 			state.flushVertexState();
+			state.flushStencilState();
 			glDrawElementsInstancedBaseVertexBaseInstance(state.m_topology,
 				m_info.m_count,
 				indicesType,
@@ -195,6 +196,7 @@ void CommandBufferImpl::drawArrays(U32 count, U32 instanceCount, U32 first, U32
 		Error operator()(GlState& state)
 		{
 			state.flushVertexState();
+			state.flushStencilState();
 			glDrawArraysInstancedBaseInstance(
 				state.m_topology, m_info.m_first, m_info.m_count, m_info.m_instanceCount, m_info.m_baseInstance);
 
@@ -231,6 +233,7 @@ void CommandBufferImpl::drawElementsIndirect(U32 drawCount, PtrSize offset, Buff
 		Error operator()(GlState& state)
 		{
 			state.flushVertexState();
+			state.flushStencilState();
 			const BufferImpl& buff = m_buff->getImplementation();
 
 			ANKI_ASSERT(m_offset + sizeof(DrawElementsIndirectInfo) * m_drawCount <= buff.m_size);
@@ -287,6 +290,7 @@ void CommandBufferImpl::drawArraysIndirect(U32 drawCount, PtrSize offset, Buffer
 		Error operator()(GlState& state)
 		{
 			state.flushVertexState();
+			state.flushStencilState();
 			const BufferImpl& buff = m_buff->getImplementation();
 
 			ANKI_ASSERT(m_offset + sizeof(DrawArraysIndirectInfo) * m_drawCount <= buff.m_size);

+ 37 - 0
src/anki/gr/gl/Common.cpp

@@ -42,6 +42,43 @@ GLenum convertCompareOperation(CompareOperation in)
 	return out;
 }
 
+GLenum convertStencilOperation(StencilOperation in)
+{
+	GLenum out = GL_NONE;
+
+	switch(in)
+	{
+	case StencilOperation::KEEP:
+		out = GL_KEEP;
+		break;
+	case StencilOperation::ZERO:
+		out = GL_ZERO;
+		break;
+	case StencilOperation::REPLACE:
+		out = GL_REPLACE;
+		break;
+	case StencilOperation::INCREMENT_AND_CLAMP:
+		out = GL_INCR;
+		break;
+	case StencilOperation::DECREMENT_AND_CLAMP:
+		out = GL_DECR;
+		break;
+	case StencilOperation::INVERT:
+		out = GL_INVERT;
+		break;
+	case StencilOperation::INCREMENT_AND_WRAP:
+		out = GL_INCR_WRAP;
+		break;
+	case StencilOperation::DECREMENT_AND_WRAP:
+		out = GL_DECR_WRAP;
+		break;
+	default:
+		ANKI_ASSERT(0);
+	}
+
+	return out;
+}
+
 void convertFilter(SamplingFilter minMagFilter, SamplingFilter mipFilter, GLenum& minFilter, GLenum& magFilter)
 {
 	switch(minMagFilter)

+ 18 - 0
src/anki/gr/gl/Common.h

@@ -38,6 +38,24 @@ const U MAX_STORAGE_BLOCK_SIZE = 2 << 27;
 /// Converter.
 GLenum convertCompareOperation(CompareOperation in);
 
+GLenum convertStencilOperation(StencilOperation in);
+
+inline GLenum convertFaceMode(FaceSelectionMask in)
+{
+	if(in == FaceSelectionMask::FRONT)
+	{
+		return GL_FRONT;
+	}
+	else if(in == FaceSelectionMask::BACK)
+	{
+		return GL_BACK;
+	}
+	else
+	{
+		return GL_FRONT_AND_BACK;
+	}
+}
+
 void convertFilter(SamplingFilter minMagFilter, SamplingFilter mipFilter, GLenum& minFilter, GLenum& magFilter);
 /// @}
 

+ 25 - 24
src/anki/gr/gl/Framebuffer.cpp

@@ -20,33 +20,34 @@ Framebuffer::~Framebuffer()
 {
 }
 
-class CreateFramebufferCommand final : public GlCommand
+void Framebuffer::init(const FramebufferInitInfo& init)
 {
-public:
-	FramebufferPtr m_fb;
-	FramebufferInitInfo m_init;
-
-	CreateFramebufferCommand(Framebuffer* handle, const FramebufferInitInfo& init)
-		: m_fb(handle)
-		, m_init(init)
-	{
-	}
-
-	Error operator()(GlState&)
+	class CreateFramebufferCommand final : public GlCommand
 	{
-		FramebufferImpl& impl = m_fb->getImplementation();
-		Error err = impl.init(m_init);
+	public:
+		FramebufferPtr m_fb;
+		FramebufferInitInfo m_init;
+
+		CreateFramebufferCommand(Framebuffer* handle, const FramebufferInitInfo& init)
+			: m_fb(handle)
+			, m_init(init)
+		{
+		}
+
+		Error operator()(GlState&)
+		{
+			FramebufferImpl& impl = m_fb->getImplementation();
+			Error err = impl.init(m_init);
+
+			GlObject::State oldState =
+				impl.setStateAtomically((err) ? GlObject::State::ERROR : GlObject::State::CREATED);
+			ANKI_ASSERT(oldState == GlObject::State::TO_BE_CREATED);
+			(void)oldState;
+
+			return err;
+		}
+	};
 
-		GlObject::State oldState = impl.setStateAtomically((err) ? GlObject::State::ERROR : GlObject::State::CREATED);
-		ANKI_ASSERT(oldState == GlObject::State::TO_BE_CREATED);
-		(void)oldState;
-
-		return err;
-	}
-};
-
-void Framebuffer::init(const FramebufferInitInfo& init)
-{
 	m_impl.reset(getAllocator().newInstance<FramebufferImpl>(&getManager()));
 
 	CommandBufferPtr cmdb = getManager().newInstance<CommandBuffer>(CommandBufferInitInfo());

+ 62 - 7
src/anki/gr/gl/FramebufferImpl.cpp

@@ -53,9 +53,44 @@ Error FramebufferImpl::init(const FramebufferInitInfo& init)
 	if(m_in.m_depthStencilAttachment.m_texture.isCreated())
 	{
 		const FramebufferAttachmentInfo& att = m_in.m_depthStencilAttachment;
-		const GLenum binding = GL_DEPTH_ATTACHMENT;
+		const TextureImpl& tex = att.m_texture->getImplementation();
+		ANKI_ASSERT((tex.m_dsAspect & att.m_aspect) == att.m_aspect);
 
-		attachTextureInternal(binding, att.m_texture->getImplementation(), att);
+		GLenum binding;
+		if(att.m_aspect == DepthStencilAspectMask::DEPTH)
+		{
+			ANKI_ASSERT(tex.m_format == GL_DEPTH_COMPONENT || tex.m_format == GL_DEPTH_STENCIL);
+			binding = GL_DEPTH_ATTACHMENT;
+			m_dsAspect = att.m_aspect;
+		}
+		else if(att.m_aspect == DepthStencilAspectMask::STENCIL)
+		{
+			ANKI_ASSERT(tex.m_format == GL_STENCIL_INDEX || tex.m_format == GL_DEPTH_STENCIL);
+			binding = GL_STENCIL_ATTACHMENT;
+			m_dsAspect = att.m_aspect;
+		}
+		else if(att.m_aspect == DepthStencilAspectMask::DEPTH_STENCIL)
+		{
+			ANKI_ASSERT(tex.m_format == GL_DEPTH_STENCIL);
+			binding = GL_DEPTH_STENCIL_ATTACHMENT;
+			m_dsAspect = att.m_aspect;
+		}
+		else if(tex.m_format == GL_DEPTH_COMPONENT)
+		{
+			binding = GL_DEPTH_ATTACHMENT;
+			m_dsAspect = DepthStencilAspectMask::DEPTH;
+		}
+		else if(tex.m_format == GL_STENCIL_INDEX)
+		{
+			binding = GL_STENCIL_ATTACHMENT;
+			m_dsAspect = DepthStencilAspectMask::STENCIL;
+		}
+		else
+		{
+			ANKI_ASSERT(!"Need to set FramebufferAttachmentInfo::m_aspect");
+		}
+
+		attachTextureInternal(binding, tex, att);
 
 		if(att.m_loadOperation == AttachmentLoadOperation::DONT_CARE)
 		{
@@ -159,8 +194,7 @@ void FramebufferImpl::bind(const GlState& state)
 
 			if(att.m_loadOperation == AttachmentLoadOperation::CLEAR)
 			{
-				// Enable write mask in case a pipeline changed it (else no
-				// clear will happen) and then restore state
+				// Enable write mask in case a pipeline changed it (else no clear will happen) and then restore state
 				Bool restore = false;
 				if(state.m_colorWriteMasks[i][0] != true || state.m_colorWriteMasks[i][1] != true
 					|| state.m_colorWriteMasks[i][2] != true
@@ -183,11 +217,11 @@ void FramebufferImpl::bind(const GlState& state)
 			}
 		}
 
-		if(m_in.m_depthStencilAttachment.m_texture.isCreated()
+		// Clear depth
+		if(!!(m_dsAspect & DepthStencilAspectMask::DEPTH) && m_in.m_depthStencilAttachment.m_texture.isCreated()
 			&& m_in.m_depthStencilAttachment.m_loadOperation == AttachmentLoadOperation::CLEAR)
 		{
-			// Enable write mask in case a pipeline changed it (else no
-			// clear will happen) and then restore state
+			// Enable write mask in case a pipeline changed it (else no clear will happen) and then restore state
 			if(state.m_depthWriteMask == false)
 			{
 				glDepthMask(true);
@@ -200,6 +234,27 @@ void FramebufferImpl::bind(const GlState& state)
 				glDepthMask(false);
 			}
 		}
+
+		// Clear stencil
+		if(!!(m_dsAspect & DepthStencilAspectMask::STENCIL) && m_in.m_depthStencilAttachment.m_texture.isCreated()
+			&& m_in.m_depthStencilAttachment.m_stencilLoadOperation == AttachmentLoadOperation::CLEAR)
+		{
+			// Enable write mask in case a pipeline changed it (else no clear will happen) and then restore state
+			// From the spec: The clear operation always uses the front stencil write mask when clearing the stencil
+			// buffer
+			if(state.m_stencilWriteMask[0] != MAX_U32)
+			{
+				glStencilMaskSeparate(GL_FRONT, MAX_U32);
+			}
+
+			GLuint clearVal = m_in.m_depthStencilAttachment.m_clearValue.m_depthStencil.m_stencil;
+			glClearBufferuiv(GL_STENCIL, 0, &clearVal);
+
+			if(state.m_stencilWriteMask[0] != MAX_U32)
+			{
+				glStencilMaskSeparate(GL_FRONT, state.m_stencilWriteMask[0]);
+			}
+		}
 	}
 }
 

+ 1 - 0
src/anki/gr/gl/FramebufferImpl.h

@@ -42,6 +42,7 @@ private:
 	Array<GLenum, MAX_COLOR_ATTACHMENTS + 1> m_invalidateBuffers;
 	U8 m_invalidateBuffersCount = 0;
 	Bool8 m_bindDefault = false;
+	DepthStencilAspectMask m_dsAspect = DepthStencilAspectMask::NONE;
 
 	/// Attach a texture
 	static void attachTextureInternal(GLenum attachment, const TextureImpl& tex, const FramebufferAttachmentInfo& info);

+ 15 - 0
src/anki/gr/gl/GlState.cpp

@@ -160,4 +160,19 @@ void GlState::flushVertexState()
 	}
 }
 
+void GlState::flushStencilState()
+{
+	if(m_glStencilFuncSeparateDirtyMask & 1)
+	{
+		glStencilFuncSeparate(GL_FRONT, m_stencilCompareFunc[0], m_stencilRef[0], m_stencilCompareMask[0]);
+		m_glStencilFuncSeparateDirtyMask &= ~1u;
+	}
+
+	if(m_glStencilFuncSeparateDirtyMask & 2)
+	{
+		glStencilFuncSeparate(GL_BACK, m_stencilCompareFunc[1], m_stencilRef[1], m_stencilCompareMask[1]);
+		m_glStencilFuncSeparateDirtyMask &= ~2u;
+	}
+}
+
 } // end namespace anki

+ 9 - 0
src/anki/gr/gl/GlState.h

@@ -66,6 +66,14 @@ public:
 		{{true, true, true, true}},
 		{{true, true, true, true}}}};
 	Bool m_depthWriteMask = true;
+
+	// Stencil
+	Array<GLenum, 2> m_stencilCompareFunc = {{GL_ALWAYS, GL_ALWAYS}}; ///< Pipeline sets it
+	Array<U32, 2> m_stencilRef = {{0, 0}}; ///< CommandBuffer sets it
+	Array<U32, 2> m_stencilCompareMask = {{MAX_U32, MAX_U32}}; ///< CommandBuffer sets it
+	U8 m_glStencilFuncSeparateDirtyMask = 0;
+
+	Array<U32, 2> m_stencilWriteMask = {{MAX_U32, MAX_U32}}; ///< Framebuffer wants that.
 	/// @}
 
 	GlState(GrManager* manager)
@@ -83,6 +91,7 @@ public:
 	void destroy();
 
 	void flushVertexState();
+	void flushStencilState();
 };
 /// @}
 

+ 58 - 5
src/anki/gr/gl/PipelineImpl.cpp

@@ -6,6 +6,7 @@
 #include <anki/gr/gl/PipelineImpl.h>
 #include <anki/gr/gl/ShaderImpl.h>
 #include <anki/gr/gl/GlState.h>
+#include <anki/gr/common/Misc.h>
 #include <anki/util/Logger.h>
 #include <anki/util/Hash.h>
 
@@ -345,13 +346,13 @@ void PipelineImpl::initRasterizerState()
 
 	switch(m_in.m_rasterizer.m_cullMode)
 	{
-	case CullMode::FRONT:
+	case FaceSelectionMask::FRONT:
 		m_cache.m_cullMode = GL_FRONT;
 		break;
-	case CullMode::BACK:
+	case FaceSelectionMask::BACK:
 		m_cache.m_cullMode = GL_BACK;
 		break;
-	case CullMode::FRONT_AND_BACK:
+	case FaceSelectionMask::FRONT_AND_BACK:
 		m_cache.m_cullMode = GL_FRONT_AND_BACK;
 		break;
 	default:
@@ -363,9 +364,32 @@ void PipelineImpl::initRasterizerState()
 
 void PipelineImpl::initDepthStencilState()
 {
-	m_cache.m_depthCompareFunction = convertCompareOperation(m_in.m_depthStencil.m_depthCompareFunction);
+	const auto& ds = m_in.m_depthStencil;
 
-	m_hashes.m_depthStencil = computeHash(&m_in.m_depthStencil, sizeof(m_in.m_depthStencil));
+	// Depth
+	m_cache.m_depthCompareFunction = convertCompareOperation(ds.m_depthCompareFunction);
+
+	// Stencil
+	m_cache.m_stencilFailOp[0] = convertStencilOperation(ds.m_stencilFront.m_stencilFailOperation);
+	m_cache.m_stencilPassDepthFailOp[0] = convertStencilOperation(ds.m_stencilFront.m_stencilPassDepthFailOperation);
+	m_cache.m_stencilPassDepthPassOp[0] = convertStencilOperation(ds.m_stencilFront.m_stencilPassDepthPassOperation);
+	m_cache.m_stencilCompareFunc[0] = convertCompareOperation(ds.m_stencilFront.m_compareFunction);
+
+	m_cache.m_stencilFailOp[1] = convertStencilOperation(ds.m_stencilBack.m_stencilFailOperation);
+	m_cache.m_stencilPassDepthFailOp[1] = convertStencilOperation(ds.m_stencilBack.m_stencilPassDepthFailOperation);
+	m_cache.m_stencilPassDepthPassOp[1] = convertStencilOperation(ds.m_stencilBack.m_stencilPassDepthPassOperation);
+	m_cache.m_stencilCompareFunc[1] = convertCompareOperation(ds.m_stencilBack.m_compareFunction);
+
+	if(stencilTestDisabled(ds.m_stencilFront) && stencilTestDisabled(ds.m_stencilBack))
+	{
+		m_stencilTestEnabled = false;
+	}
+	else
+	{
+		m_stencilTestEnabled = true;
+	}
+
+	m_hashes.m_depthStencil = computeHash(&ds, sizeof(ds));
 }
 
 void PipelineImpl::initColorState()
@@ -502,6 +526,7 @@ void PipelineImpl::setDepthStencilState(GlState& state) const
 
 	state.m_stateHashes.m_depthStencil = m_hashes.m_depthStencil;
 
+	// Depth
 	glDepthMask(m_in.m_depthStencil.m_depthWriteEnabled);
 	state.m_depthWriteMask = m_in.m_depthStencil.m_depthWriteEnabled;
 
@@ -515,6 +540,34 @@ void PipelineImpl::setDepthStencilState(GlState& state) const
 	}
 
 	glDepthFunc(m_cache.m_depthCompareFunction);
+
+	// Stencil
+	if(m_stencilTestEnabled)
+	{
+		glEnable(GL_STENCIL_TEST);
+	}
+	else
+	{
+		glDisable(GL_STENCIL_TEST);
+	}
+
+	glStencilOpSeparate(
+		GL_FRONT, m_cache.m_stencilFailOp[0], m_cache.m_stencilPassDepthFailOp[0], m_cache.m_stencilPassDepthPassOp[0]);
+
+	glStencilOpSeparate(
+		GL_BACK, m_cache.m_stencilFailOp[1], m_cache.m_stencilPassDepthFailOp[1], m_cache.m_stencilPassDepthPassOp[1]);
+
+	if(state.m_stencilCompareFunc[0] != m_cache.m_stencilCompareFunc[0])
+	{
+		state.m_stencilCompareFunc[0] = m_cache.m_stencilCompareFunc[0];
+		state.m_glStencilFuncSeparateDirtyMask |= 1 << 0;
+	}
+
+	if(state.m_stencilCompareFunc[1] != m_cache.m_stencilCompareFunc[1])
+	{
+		state.m_stencilCompareFunc[1] = m_cache.m_stencilCompareFunc[1];
+		state.m_glStencilFuncSeparateDirtyMask |= 1 << 1;
+	}
 }
 
 void PipelineImpl::setColorState(GlState& state) const

+ 6 - 0
src/anki/gr/gl/PipelineImpl.h

@@ -51,6 +51,7 @@ private:
 	Bool8 m_compute = false; ///< Is compute
 	Bool8 m_tessellation = false;
 	Bool8 m_blendEnabled = false;
+	Bool8 m_stencilTestEnabled = false;
 
 	/// Input values.
 	PipelineInitInfo m_in;
@@ -66,6 +67,11 @@ private:
 		Bool8 m_depthWrite = false;
 		GLenum m_depthCompareFunction = 0;
 		Array<Attachment, MAX_COLOR_ATTACHMENTS> m_attachments;
+
+		Array<GLenum, 2> m_stencilFailOp = {{0, 0}};
+		Array<GLenum, 2> m_stencilPassDepthFailOp = {{0, 0}};
+		Array<GLenum, 2> m_stencilPassDepthPassOp = {{0, 0}};
+		Array<GLenum, 2> m_stencilCompareFunc = {{0, 0}};
 	} m_cache;
 
 	/// State hashes.

+ 41 - 8
src/anki/gr/gl/TextureImpl.cpp

@@ -44,8 +44,12 @@ static GLenum convertTextureType(TextureType type)
 	return out;
 }
 
-static void convertTextureInformation(
-	const PixelFormat& pf, Bool8& compressed, GLenum& format, GLenum& internalFormat, GLenum& type)
+static void convertTextureInformation(const PixelFormat& pf,
+	Bool8& compressed,
+	GLenum& format,
+	GLenum& internalFormat,
+	GLenum& type,
+	DepthStencilAspectMask& dsAspect)
 {
 	compressed =
 		pf.m_components >= ComponentFormat::FIRST_COMPRESSED && pf.m_components <= ComponentFormat::LAST_COMPRESSED;
@@ -235,11 +239,13 @@ static void convertTextureInformation(
 		format = GL_DEPTH_COMPONENT;
 		internalFormat = GL_DEPTH_COMPONENT24;
 		type = GL_UNSIGNED_INT;
+		dsAspect = DepthStencilAspectMask::DEPTH;
 		break;
 	case ComponentFormat::D16:
 		format = GL_DEPTH_COMPONENT;
 		internalFormat = GL_DEPTH_COMPONENT16;
 		type = GL_UNSIGNED_SHORT;
+		dsAspect = DepthStencilAspectMask::DEPTH;
 		break;
 	default:
 		ANKI_ASSERT(0);
@@ -325,7 +331,7 @@ void TextureImpl::preInit(const TextureInitInfo& init)
 	m_texType = init.m_type;
 	m_pformat = init.m_format;
 
-	convertTextureInformation(init.m_format, m_compressed, m_format, m_internalFormat, m_type);
+	convertTextureInformation(init.m_format, m_compressed, m_format, m_internalFormat, m_type, m_dsAspect);
 
 	if(m_target != GL_TEXTURE_3D)
 	{
@@ -450,11 +456,13 @@ void TextureImpl::init(const TextureInitInfo& init)
 	ANKI_CHECK_GL_ERROR();
 }
 
-void TextureImpl::writeSurface(const TextureSurfaceInfo& surf, void* data, PtrSize dataSize)
+void TextureImpl::writeSurface(
+	const TextureSurfaceInfo& surf, void* data, PtrSize dataSize, DepthStencilAspectMask aspect)
 {
 	checkSurface(surf);
 	ANKI_ASSERT(data);
 	ANKI_ASSERT(dataSize > 0);
+	ANKI_ASSERT(!aspect && "TODO");
 
 	U mipmap = surf.m_level;
 	U surfIdx = computeSurfaceIdx(surf);
@@ -506,12 +514,13 @@ void TextureImpl::writeSurface(const TextureSurfaceInfo& surf, void* data, PtrSi
 	ANKI_CHECK_GL_ERROR();
 }
 
-void TextureImpl::writeVolume(const TextureVolumeInfo& vol, void* data, PtrSize dataSize)
+void TextureImpl::writeVolume(const TextureVolumeInfo& vol, void* data, PtrSize dataSize, DepthStencilAspectMask aspect)
 {
 	checkVolume(vol);
 	ANKI_ASSERT(data);
 	ANKI_ASSERT(dataSize > 0);
 	ANKI_ASSERT(m_texType == TextureType::_3D);
+	ANKI_ASSERT(!aspect && "TODO");
 
 	U mipmap = vol.m_level;
 	U w = m_width >> mipmap;
@@ -535,10 +544,11 @@ void TextureImpl::writeVolume(const TextureVolumeInfo& vol, void* data, PtrSize
 	ANKI_CHECK_GL_ERROR();
 }
 
-void TextureImpl::generateMipmaps2d(U face, U layer)
+void TextureImpl::generateMipmaps2d(U face, U layer, DepthStencilAspectMask aspect)
 {
 	U surface = computeSurfaceIdx(TextureSurfaceInfo(0, 0, face, layer));
 	ANKI_ASSERT(!m_compressed);
+	ANKI_ASSERT(aspect == m_dsAspect && "For now");
 
 	if(m_surfaceCountPerLevel > 1)
 	{
@@ -613,17 +623,40 @@ void TextureImpl::copy(const TextureImpl& src,
 		1);
 }
 
-void TextureImpl::clear(const TextureSurfaceInfo& surf, const ClearValue& clearValue)
+void TextureImpl::clear(const TextureSurfaceInfo& surf, const ClearValue& clearValue, DepthStencilAspectMask aspect)
 {
 	ANKI_ASSERT(isCreated());
 	ANKI_ASSERT(surf.m_level < m_mipsCount);
+	ANKI_ASSERT((aspect & m_dsAspect) == aspect);
+
+	// Find the aspect to clear
+	GLenum format;
+	if(aspect == DepthStencilAspectMask::DEPTH)
+	{
+		ANKI_ASSERT(m_format == GL_DEPTH_COMPONENT || m_format == GL_DEPTH_STENCIL);
+		format = GL_DEPTH_COMPONENT;
+	}
+	else if(aspect == DepthStencilAspectMask::STENCIL)
+	{
+		ANKI_ASSERT(m_format == GL_STENCIL_INDEX || m_format == GL_DEPTH_STENCIL);
+		format = GL_STENCIL_INDEX;
+	}
+	else if(aspect == DepthStencilAspectMask::DEPTH_STENCIL)
+	{
+		ANKI_ASSERT(m_format == GL_DEPTH_STENCIL);
+		format = GL_DEPTH_STENCIL;
+	}
+	else
+	{
+		format = m_format;
+	}
 
 	U surfaceIdx = computeSurfaceIdx(surf);
 	U width = m_width >> surf.m_level;
 	U height = m_height >> surf.m_level;
 
 	glClearTexSubImage(
-		m_glName, surf.m_level, 0, 0, surfaceIdx, width, height, 1, m_format, GL_FLOAT, &clearValue.m_colorf[0]);
+		m_glName, surf.m_level, 0, 0, surfaceIdx, width, height, 1, format, GL_FLOAT, &clearValue.m_colorf[0]);
 }
 
 U TextureImpl::computeSurfaceIdx(const TextureSurfaceInfo& surf) const

+ 5 - 4
src/anki/gr/gl/TextureImpl.h

@@ -34,6 +34,7 @@ public:
 	Bool8 m_compressed = false;
 	PixelFormat m_pformat;
 	DynamicArray<GLuint> m_texViews; ///< Temp views for gen mips.
+	DepthStencilAspectMask m_dsAspect = DepthStencilAspectMask::NONE;
 
 	TextureImpl(GrManager* manager)
 		: GlObject(manager)
@@ -60,13 +61,13 @@ public:
 	void init(const TextureInitInfo& init);
 
 	/// Write texture data.
-	void writeSurface(const TextureSurfaceInfo& surf, void* data, PtrSize dataSize);
+	void writeSurface(const TextureSurfaceInfo& surf, void* data, PtrSize dataSize, DepthStencilAspectMask aspect);
 
 	/// Write texture data.
-	void writeVolume(const TextureVolumeInfo& vol, void* data, PtrSize dataSize);
+	void writeVolume(const TextureVolumeInfo& vol, void* data, PtrSize dataSize, DepthStencilAspectMask aspect);
 
 	/// Generate mipmaps.
-	void generateMipmaps2d(U face, U layer);
+	void generateMipmaps2d(U face, U layer, DepthStencilAspectMask aspect);
 
 	/// Copy a single surface from one texture to another.
 	static void copy(const TextureImpl& src,
@@ -76,7 +77,7 @@ public:
 
 	void bind();
 
-	void clear(const TextureSurfaceInfo& surf, const ClearValue& clearValue);
+	void clear(const TextureSurfaceInfo& surf, const ClearValue& clearValue, DepthStencilAspectMask aspect);
 
 	U computeSurfaceIdx(const TextureSurfaceInfo& surf) const;
 };

+ 1 - 1
src/anki/renderer/Ir.cpp

@@ -311,7 +311,7 @@ Error Ir::initIs()
 	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_rasterizer.m_cullMode = FaceSelectionMask::FRONT;
 
 	pinit.m_depthStencil.m_depthWriteEnabled = false;
 	pinit.m_depthStencil.m_depthCompareFunction = CompareOperation::GREATER;