瀏覽代碼

VK & D3D: Refactor the graphics state tracker

Panagiotis Christopoulos Charitos 1 年之前
父節點
當前提交
fb229e1f36

+ 15 - 10
AnKi/Gr/BackendCommon/Functions.h

@@ -10,20 +10,25 @@
 
 namespace anki {
 
-inline Bool stencilTestDisabled(StencilOperation stencilFail, StencilOperation stencilPassDepthFail, StencilOperation stencilPassDepthPass,
-								CompareOperation compare)
+inline Bool stencilTestEnabled(StencilOperation stencilFail, StencilOperation stencilPassDepthFail, StencilOperation stencilPassDepthPass,
+							   CompareOperation compare)
 {
-	return stencilFail == StencilOperation::kKeep && stencilPassDepthFail == StencilOperation::kKeep
-		   && stencilPassDepthPass == StencilOperation::kKeep && compare == CompareOperation::kAlways;
+	return stencilFail != StencilOperation::kKeep || stencilPassDepthFail != StencilOperation::kKeep
+		   || stencilPassDepthPass != StencilOperation::kKeep || compare != CompareOperation::kAlways;
 }
 
-inline Bool blendingDisabled(BlendFactor srcFactorRgb, BlendFactor dstFactorRgb, BlendFactor srcFactorA, BlendFactor dstFactorA, BlendOperation opRgb,
-							 BlendOperation opA)
+inline Bool depthTestEnabled(CompareOperation depthCompare, Bool depthWrite)
 {
-	Bool dontWantBlend = srcFactorRgb == BlendFactor::kOne && dstFactorRgb == BlendFactor::kZero && srcFactorA == BlendFactor::kOne
-						 && dstFactorA == BlendFactor::kZero && (opRgb == BlendOperation::kAdd || opRgb == BlendOperation::kSubtract)
-						 && (opA == BlendOperation::kAdd || opA == BlendOperation::kSubtract);
-	return dontWantBlend;
+	return depthCompare != CompareOperation::kAlways || depthWrite;
+}
+
+inline Bool blendingEnabled(BlendFactor srcFactorRgb, BlendFactor dstFactorRgb, BlendFactor srcFactorA, BlendFactor dstFactorA, BlendOperation opRgb,
+							BlendOperation opA)
+{
+	const Bool dontWantBlend = srcFactorRgb == BlendFactor::kOne && dstFactorRgb == BlendFactor::kZero && srcFactorA == BlendFactor::kOne
+							   && dstFactorA == BlendFactor::kZero && (opRgb == BlendOperation::kAdd || opRgb == BlendOperation::kSubtract)
+							   && (opA == BlendOperation::kAdd || opA == BlendOperation::kSubtract);
+	return !dontWantBlend;
 }
 
 /// Using an AnKi typename get the ShaderVariableDataType. Used for debugging.

+ 138 - 0
AnKi/Gr/BackendCommon/GraphicsStateTracker.cpp

@@ -0,0 +1,138 @@
+// Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <AnKi/Gr/BackendCommon/GraphicsStateTracker.h>
+
+namespace anki {
+
+Bool GraphicsStateTracker::updateHashes()
+{
+	Bool someHashWasDirty = false;
+
+	if(m_hashes.m_vert == 0)
+	{
+		if(m_staticState.m_vert.m_activeAttribs.getAnySet())
+		{
+			m_hashes.m_vert = 0xC0FEE;
+
+			for(VertexAttributeSemantic i : EnumIterable<VertexAttributeSemantic>())
+			{
+				if(!m_staticState.m_vert.m_activeAttribs.get(i))
+				{
+					continue;
+				}
+
+				ANKI_ASSERT(m_staticState.m_vert.m_attribsSetMask.get(i) && "Forgot to set the vert attribute");
+				m_hashes.m_vert = appendObjectHash(m_staticState.m_vert.m_attribs[i], m_hashes.m_vert);
+
+				ANKI_ASSERT(m_staticState.m_vert.m_bindingsSetMask.get(m_staticState.m_vert.m_attribs[i].m_binding)
+							&& "Forgot to inform about the vert binding");
+				m_hashes.m_vert = appendObjectHash(m_staticState.m_vert.m_bindings[m_staticState.m_vert.m_attribs[i].m_binding], m_hashes.m_vert);
+			}
+		}
+		else
+		{
+			m_hashes.m_vert = 0xC0FEE;
+		}
+
+		someHashWasDirty = true;
+	}
+
+	if(m_hashes.m_rast == 0)
+	{
+		static_assert(sizeof(m_staticState.m_rast) < kMaxU64);
+		memcpy(&m_hashes.m_rast, &m_staticState.m_rast, sizeof(m_staticState.m_rast));
+		++m_hashes.m_rast; // Because m_staticState.m_rast may be zero
+		someHashWasDirty = true;
+	}
+
+	if(m_hashes.m_ia == 0)
+	{
+		static_assert(sizeof(m_staticState.m_ia) < kMaxU64);
+		memcpy(&m_hashes.m_ia, &m_staticState.m_ia, sizeof(m_staticState.m_ia));
+		++m_hashes.m_ia; // Because m_staticState.m_ia may be zero
+		someHashWasDirty = true;
+	}
+
+	if(m_hashes.m_depthStencil == 0)
+	{
+		const Bool hasStencil =
+			m_staticState.m_misc.m_depthStencilFormat != Format::kNone && getFormatInfo(m_staticState.m_misc.m_depthStencilFormat).isStencil();
+
+		const Bool hasDepth =
+			m_staticState.m_misc.m_depthStencilFormat != Format::kNone && getFormatInfo(m_staticState.m_misc.m_depthStencilFormat).isDepth();
+
+		m_hashes.m_depthStencil = 0xC0FEE;
+
+		if(hasStencil)
+		{
+			m_hashes.m_depthStencil = appendObjectHash(m_staticState.m_stencil, m_hashes.m_depthStencil);
+		}
+
+		if(hasDepth)
+		{
+			m_hashes.m_depthStencil = appendObjectHash(m_staticState.m_depth, m_hashes.m_depthStencil);
+		}
+
+		someHashWasDirty = true;
+	}
+
+	if(m_hashes.m_blend == 0)
+	{
+		if(m_staticState.m_misc.m_colorRtMask.getAnySet())
+		{
+			m_hashes.m_blend = m_staticState.m_blend.m_alphaToCoverage;
+
+			for(U32 i = 0; i < kMaxColorRenderTargets; ++i)
+			{
+				if(m_staticState.m_misc.m_colorRtMask.get(i))
+				{
+					m_hashes.m_blend = appendObjectHash(m_staticState.m_blend.m_colorRts[i], m_hashes.m_blend);
+				}
+			}
+		}
+		else
+		{
+			m_hashes.m_blend = 0xC0FFE;
+		}
+
+		someHashWasDirty = true;
+	}
+
+	if(m_hashes.m_misc == 0)
+	{
+		m_hashes.m_misc = computeObjectHash(m_staticState.m_misc);
+		someHashWasDirty = true;
+	}
+
+	if(m_hashes.m_shaderProg == 0)
+	{
+		m_hashes.m_shaderProg = m_staticState.m_shaderProg->getUuid();
+		someHashWasDirty = true;
+	}
+
+	if(!someHashWasDirty)
+	{
+		ANKI_ASSERT(m_globalHash == computeObjectHash(m_hashes));
+		return false;
+	}
+	else
+	{
+		// Compute complete hash
+		const U64 globalHash = computeObjectHash(m_hashes);
+
+		if(globalHash != m_globalHash)
+		{
+			m_globalHash = globalHash;
+			return true;
+		}
+		else
+		{
+			return false;
+		}
+	}
+}
+
+} // end namespace anki

+ 629 - 0
AnKi/Gr/BackendCommon/GraphicsStateTracker.h

@@ -0,0 +1,629 @@
+// Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <AnKi/Gr/BackendCommon/Common.h>
+#include <AnKi/Gr/ShaderProgram.h>
+
+namespace anki {
+
+/// @addtogroup graphics
+/// @{
+
+/// This is the way the command buffer sets the graphics state.
+class GraphicsStateTracker
+{
+	friend class GraphicsPipelineFactory;
+
+public:
+	void bindVertexBuffer(U32 binding, VertexStepRate stepRate
+#if ANKI_GR_BACKEND_VULKAN
+						  ,
+						  U32 stride
+#endif
+	)
+	{
+		auto& vertState = m_staticState.m_vert;
+		if(!vertState.m_bindingsSetMask.get(binding) || vertState.m_bindings[binding].m_stepRate != stepRate
+#if ANKI_GR_BACKEND_VULKAN
+		   || vertState.m_bindings[binding].m_stride != stride
+#endif
+		)
+		{
+			vertState.m_bindingsSetMask.set(binding);
+#if ANKI_GR_BACKEND_VULKAN
+			vertState.m_bindings[binding].m_stride = stride;
+#endif
+			vertState.m_bindings[binding].m_stepRate = stepRate;
+			m_hashes.m_vert = 0;
+		}
+	}
+
+	void setVertexAttribute(VertexAttributeSemantic attribute, U32 buffBinding, Format fmt, U32 relativeOffset)
+	{
+		auto& attr = m_staticState.m_vert.m_attribs[attribute];
+		if(!m_staticState.m_vert.m_attribsSetMask.get(attribute) || attr.m_fmt != fmt || attr.m_binding != buffBinding
+		   || attr.m_relativeOffset != relativeOffset)
+		{
+			attr.m_fmt = fmt;
+			attr.m_binding = buffBinding;
+			attr.m_relativeOffset = relativeOffset;
+			m_staticState.m_vert.m_attribsSetMask.set(attribute);
+			m_hashes.m_vert = 0;
+		}
+	}
+
+	void setFillMode(FillMode mode)
+	{
+		ANKI_ASSERT(mode < FillMode::kCount);
+		if(m_staticState.m_rast.m_fillMode != mode)
+		{
+			m_staticState.m_rast.m_fillMode = mode;
+			m_hashes.m_rast = 0;
+		}
+	}
+
+	void setCullMode(FaceSelectionBit mode)
+	{
+		if(m_staticState.m_rast.m_cullMode != mode)
+		{
+			m_staticState.m_rast.m_cullMode = mode;
+			m_hashes.m_rast = 0;
+		}
+	}
+
+	void setStencilOperations(FaceSelectionBit face, StencilOperation stencilFail, StencilOperation stencilPassDepthFail,
+							  StencilOperation stencilPassDepthPass)
+	{
+		ANKI_ASSERT(face != FaceSelectionBit::kNone);
+		auto& front = m_staticState.m_stencil.m_face[0];
+		if(!!(face & FaceSelectionBit::kFront)
+		   && (front.m_fail != stencilFail || front.m_stencilPassDepthFail != stencilPassDepthFail
+			   || front.m_stencilPassDepthPass != stencilPassDepthPass))
+		{
+			front.m_fail = stencilFail;
+			front.m_stencilPassDepthFail = stencilPassDepthFail;
+			front.m_stencilPassDepthPass = stencilPassDepthPass;
+			m_hashes.m_depthStencil = 0;
+		}
+
+		auto& back = m_staticState.m_stencil.m_face[1];
+		if(!!(face & FaceSelectionBit::kBack)
+		   && (back.m_fail != stencilFail || back.m_stencilPassDepthFail != stencilPassDepthFail
+			   || back.m_stencilPassDepthPass != stencilPassDepthPass))
+		{
+			back.m_fail = stencilFail;
+			back.m_stencilPassDepthFail = stencilPassDepthFail;
+			back.m_stencilPassDepthPass = stencilPassDepthPass;
+			m_hashes.m_depthStencil = 0;
+		}
+	}
+
+	void setStencilCompareOperation(FaceSelectionBit face, CompareOperation comp)
+	{
+		ANKI_ASSERT(face != FaceSelectionBit::kNone);
+		if(!!(face & FaceSelectionBit::kFront) && m_staticState.m_stencil.m_face[0].m_compare != comp)
+		{
+			m_staticState.m_stencil.m_face[0].m_compare = comp;
+			m_hashes.m_depthStencil = 0;
+		}
+
+		if(!!(face & FaceSelectionBit::kBack) && m_staticState.m_stencil.m_face[1].m_compare != comp)
+		{
+			m_staticState.m_stencil.m_face[1].m_compare = comp;
+			m_hashes.m_depthStencil = 0;
+		}
+	}
+
+	void setStencilCompareMask(FaceSelectionBit face, U32 mask)
+	{
+		ANKI_ASSERT(face != FaceSelectionBit::kNone);
+
+#if ANKI_GR_BACKEND_VULKAN
+		if(!!(face & FaceSelectionBit::kFront) && m_dynState.m_stencilFaces[0].m_compareMask != mask)
+		{
+			m_dynState.m_stencilFaces[0].m_compareMask = mask;
+			m_dynState.m_stencilCompareMaskDirty = true;
+		}
+
+		if(!!(face & FaceSelectionBit::kBack) && m_dynState.m_stencilFaces[1].m_compareMask != mask)
+		{
+			m_dynState.m_stencilFaces[1].m_compareMask = mask;
+			m_dynState.m_stencilCompareMaskDirty = true;
+		}
+#else
+		if(!!(face & FaceSelectionBit::kFront) && m_staticState.m_stencil.m_face[0].m_compareMask != mask)
+		{
+			m_staticState.m_stencil.m_face[0].m_compareMask = mask;
+			m_hashes.m_depthStencil = 0;
+		}
+
+		if(!!(face & FaceSelectionBit::kBack) && m_staticState.m_stencil.m_face[1].m_compareMask != mask)
+		{
+			m_staticState.m_stencil.m_face[1].m_compareMask = mask;
+			m_hashes.m_depthStencil = 0;
+		}
+#endif
+	}
+
+	void setStencilWriteMask(FaceSelectionBit face, U32 mask)
+	{
+		ANKI_ASSERT(face != FaceSelectionBit::kNone);
+
+#if ANKI_GR_BACKEND_VULKAN
+		if(!!(face & FaceSelectionBit::kFront) && m_dynState.m_stencilFaces[0].m_writeMask != mask)
+		{
+			m_dynState.m_stencilFaces[0].m_writeMask = mask;
+			m_dynState.m_stencilWriteMaskDirty = true;
+		}
+
+		if(!!(face & FaceSelectionBit::kBack) && m_dynState.m_stencilFaces[1].m_writeMask != mask)
+		{
+			m_dynState.m_stencilFaces[1].m_writeMask = mask;
+			m_dynState.m_stencilWriteMaskDirty = true;
+		}
+#else
+		if(!!(face & FaceSelectionBit::kFront) && m_staticState.m_stencil.m_face[0].m_writeMask != mask)
+		{
+			m_staticState.m_stencil.m_face[0].m_writeMask = mask;
+			m_hashes.m_depthStencil = 0;
+		}
+
+		if(!!(face & FaceSelectionBit::kBack) && m_staticState.m_stencil.m_face[1].m_writeMask != mask)
+		{
+			m_staticState.m_stencil.m_face[1].m_writeMask = mask;
+			m_hashes.m_depthStencil = 0;
+		}
+#endif
+	}
+
+	void setStencilReference(FaceSelectionBit face, U32 ref)
+	{
+		ANKI_ASSERT(face != FaceSelectionBit::kNone);
+		ANKI_ASSERT((ANKI_GR_BACKEND_VULKAN || face == FaceSelectionBit::kFrontAndBack) && "D3D only supports a single value for both sides");
+
+		if(!!(face & FaceSelectionBit::kFront) && m_dynState.m_stencilFaces[0].m_ref != ref)
+		{
+			m_dynState.m_stencilFaces[0].m_ref = ref;
+			m_dynState.m_stencilRefDirty = true;
+		}
+
+		if(!!(face & FaceSelectionBit::kBack) && m_dynState.m_stencilFaces[1].m_ref != ref)
+		{
+			m_dynState.m_stencilFaces[1].m_ref = ref;
+			m_dynState.m_stencilRefDirty = true;
+		}
+	}
+
+	void setDepthWrite(Bool enable)
+	{
+		if(m_staticState.m_depth.m_writeEnabled != enable)
+		{
+			m_staticState.m_depth.m_writeEnabled = enable;
+			m_hashes.m_depthStencil = 0;
+		}
+	}
+
+	void setDepthCompareOperation(CompareOperation op)
+	{
+		ANKI_ASSERT(op < CompareOperation::kCount);
+		if(m_staticState.m_depth.m_compare != op)
+		{
+			m_staticState.m_depth.m_compare = op;
+			m_hashes.m_depthStencil = 0;
+		}
+	}
+
+	void setPolygonOffset(F32 factor, F32 units)
+	{
+#if ANKI_GR_BACKEND_VULKAN
+		if(m_dynState.m_depthBiasConstantFactor != factor || m_dynState.m_depthBiasSlopeFactor != units)
+		{
+			m_dynState.m_depthBiasConstantFactor = factor;
+			m_dynState.m_depthBiasSlopeFactor = units;
+			m_dynState.m_depthBiasClamp = 0.0f;
+			m_dynState.m_depthBiasDirty = true;
+
+			m_staticState.m_rast.m_depthBiasEnabled = factor != 0.0f || units != 0.0f;
+		}
+#else
+		if(m_staticState.m_rast.m_depthBias != factor || m_staticState.m_rast.m_slopeScaledDepthBias != units)
+		{
+			m_staticState.m_rast.m_depthBias = factor;
+			m_staticState.m_rast.m_slopeScaledDepthBias = units;
+			m_staticState.m_rast.m_depthBiasClamp = 0.0f;
+			m_hashes.m_rast = 0;
+		}
+#endif
+	}
+
+	void setAlphaToCoverage(Bool enable)
+	{
+		if(m_staticState.m_blend.m_alphaToCoverage != enable)
+		{
+			m_staticState.m_blend.m_alphaToCoverage = enable;
+			m_hashes.m_blend = 0;
+		}
+	}
+
+	void setBlendFactors(U32 attachment, BlendFactor srcRgb, BlendFactor dstRgb, BlendFactor srcA, BlendFactor dstA)
+	{
+		auto& rt = m_staticState.m_blend.m_colorRts[attachment];
+		if(rt.m_srcRgb != srcRgb || rt.m_srcA != srcA || rt.m_dstRgb != dstRgb || rt.m_dstA != dstA)
+		{
+			rt.m_srcRgb = srcRgb;
+			rt.m_srcA = srcA;
+			rt.m_dstRgb = dstRgb;
+			rt.m_dstA = dstA;
+			m_hashes.m_blend = 0;
+		}
+	}
+
+	void setBlendOperation(U32 attachment, BlendOperation funcRgb, BlendOperation funcA)
+	{
+		auto& rt = m_staticState.m_blend.m_colorRts[attachment];
+		if(rt.m_funcRgb != funcRgb || rt.m_funcA != funcA)
+		{
+			rt.m_funcRgb = funcRgb;
+			rt.m_funcA = funcA;
+			m_hashes.m_blend = 0;
+		}
+	}
+
+	void beginRenderPass(ConstWeakArray<Format> colorFormats, Format depthStencilFormat, UVec2 rtsSize
+#if ANKI_GR_BACKEND_VULKAN
+						 ,
+						 Bool rendersToSwapchain
+#endif
+	)
+	{
+		m_staticState.m_misc.m_colorRtFormats.fill(Format::kNone);
+		m_staticState.m_misc.m_colorRtMask.unsetAll();
+		for(U8 i = 0; i < colorFormats.getSize(); ++i)
+		{
+			ANKI_ASSERT(colorFormats[i] != Format::kNone);
+			m_staticState.m_misc.m_colorRtFormats[i] = colorFormats[i];
+			m_staticState.m_misc.m_colorRtMask.set(i);
+		}
+		m_staticState.m_misc.m_depthStencilFormat = depthStencilFormat;
+
+		m_hashes.m_misc = 0; // Always mark it dirty because calling beginRenderPass is a rare occurance and we want to avoid extra checks
+
+		if(m_rtsSize != rtsSize
+#if ANKI_GR_BACKEND_VULKAN
+		   || m_staticState.m_misc.m_rendersToSwapchain != rendersToSwapchain
+#endif
+		)
+		{
+			m_rtsSize = rtsSize;
+
+			// Re-set the viewport and scissor because they depend on the size of the render targets
+			m_dynState.m_scissorDirty = true;
+			m_dynState.m_viewportDirty = true;
+		}
+
+#if ANKI_GR_BACKEND_VULKAN
+		m_staticState.m_misc.m_rendersToSwapchain = rendersToSwapchain;
+#endif
+	}
+
+	void bindShaderProgram(ShaderProgram* prog)
+	{
+		ANKI_ASSERT(prog);
+		if(prog != m_staticState.m_shaderProg)
+		{
+			const ShaderReflection& refl = prog->getReflection();
+			if(m_staticState.m_vert.m_activeAttribs != refl.m_vertex.m_vertexAttributeMask)
+			{
+				m_staticState.m_vert.m_activeAttribs = refl.m_vertex.m_vertexAttributeMask;
+				m_hashes.m_vert = 0;
+			}
+
+			if(!!(prog->getShaderTypes() & ShaderTypeBit::kVertex) && refl.m_vertex.m_vertexAttributeMask.getSetBitCount())
+			{
+				if(m_staticState.m_shaderProg)
+				{
+					const Bool semanticsChanged = m_staticState.m_shaderProg->getReflection().m_vertex.m_vkVertexAttributeLocations
+												  != refl.m_vertex.m_vkVertexAttributeLocations;
+					if(semanticsChanged)
+					{
+						m_hashes.m_vert = 0;
+					}
+				}
+
+#if ANKI_GR_BACKEND_VULKAN
+				for(VertexAttributeSemantic s : EnumIterable<VertexAttributeSemantic>())
+				{
+					m_staticState.m_vert.m_attribs[s].m_semanticToVertexAttributeLocation = refl.m_vertex.m_vkVertexAttributeLocations[s];
+				}
+#endif
+			}
+
+			if(m_staticState.m_misc.m_colorRtMask != refl.m_fragment.m_colorAttachmentWritemask)
+			{
+				m_staticState.m_misc.m_colorRtMask = refl.m_fragment.m_colorAttachmentWritemask;
+				m_hashes.m_misc = 0;
+			}
+
+			m_staticState.m_shaderProg = prog;
+			m_hashes.m_shaderProg = 0;
+		}
+	}
+
+	void setPrimitiveTopology(PrimitiveTopology topology)
+	{
+		if(m_staticState.m_ia.m_topology != topology)
+		{
+			m_staticState.m_ia.m_topology = topology;
+			m_hashes.m_ia = 0;
+#if ANKI_GR_BACKEND_DIRECT3D
+			m_dynState.m_topologyDirty = true;
+#endif
+		}
+	}
+
+	void setViewport(U32 minx, U32 miny, U32 width, U32 height)
+	{
+		if(m_dynState.m_viewport[0] != minx || m_dynState.m_viewport[1] != miny || m_dynState.m_viewport[2] != width
+		   || m_dynState.m_viewport[3] != height)
+		{
+			m_dynState.m_viewport[0] = minx;
+			m_dynState.m_viewport[1] = miny;
+			m_dynState.m_viewport[2] = width;
+			m_dynState.m_viewport[3] = height;
+			m_dynState.m_viewportDirty = true;
+
+			// Scissor needs to be re-adjusted if it's in its default size
+			m_dynState.m_scissorDirty = true;
+		}
+	}
+
+	void setScissor(U32 minx, U32 miny, U32 width, U32 height)
+	{
+		if(m_dynState.m_scissor[0] != minx || m_dynState.m_scissor[1] != miny || m_dynState.m_scissor[2] != width
+		   || m_dynState.m_scissor[3] != height)
+		{
+			m_dynState.m_scissor[0] = minx;
+			m_dynState.m_scissor[1] = miny;
+			m_dynState.m_scissor[2] = width;
+			m_dynState.m_scissor[3] = height;
+			m_dynState.m_scissorDirty = true;
+		}
+	}
+
+	void setLineWidth(F32 width)
+	{
+#if ANKI_GR_BACKEND_VULKAN
+		if(m_dynState.m_lineWidth != width)
+		{
+			m_dynState.m_lineWidth = width;
+			m_dynState.m_lineWidthDirty = true;
+		}
+#else
+		ANKI_ASSERT(width == 1.0f && "DX only accepts 1.0 line width");
+#endif
+	}
+
+#if ANKI_GR_BACKEND_VULKAN
+	void setEnablePipelineStatistics(Bool enable)
+	{
+		m_staticState.m_misc.m_pipelineStatisticsEnabled = enable;
+	}
+#endif
+
+	void setPrimitiveRestart(Bool enable)
+	{
+		if(m_staticState.m_ia.m_primitiveRestartEnabled != enable)
+		{
+			m_staticState.m_ia.m_primitiveRestartEnabled = enable;
+			m_hashes.m_ia = 0;
+		}
+	}
+
+	void setColorChannelWriteMask(U32 attachment, ColorBit mask)
+	{
+		if(m_staticState.m_blend.m_colorRts[attachment].m_channelWriteMask != mask)
+		{
+			m_staticState.m_blend.m_colorRts[attachment].m_channelWriteMask = mask;
+			m_hashes.m_blend = 0;
+		}
+	}
+
+	ShaderProgram& getShaderProgram()
+	{
+		ANKI_ASSERT(m_staticState.m_shaderProg);
+		return *m_staticState.m_shaderProg;
+	}
+
+private:
+	ANKI_BEGIN_PACKED_STRUCT
+	class StaticState
+	{
+	public:
+		class Vertex
+		{
+		public:
+			class Binding
+			{
+			public:
+#if ANKI_GR_BACKEND_VULKAN
+				U32 m_stride;
+#endif
+				VertexStepRate m_stepRate;
+
+				Binding()
+				{
+					// No init for opt
+				}
+			};
+
+			class Attribute
+			{
+			public:
+				U32 m_relativeOffset;
+				Format m_fmt;
+				U32 m_binding;
+#if ANKI_GR_BACKEND_VULKAN
+				U8 m_semanticToVertexAttributeLocation;
+#endif
+
+				Attribute()
+				{
+					// No init for opt
+				}
+			};
+
+			Array<Binding, U32(VertexAttributeSemantic::kCount)> m_bindings;
+			Array<Attribute, U32(VertexAttributeSemantic::kCount)> m_attribs;
+			BitSet<U32(VertexAttributeSemantic::kCount)> m_activeAttribs = false;
+
+			BitSet<U32(VertexAttributeSemantic::kCount)> m_bindingsSetMask = false;
+			BitSet<U32(VertexAttributeSemantic::kCount)> m_attribsSetMask = false;
+		} m_vert;
+
+		class InputAssembler
+		{
+		public:
+			PrimitiveTopology m_topology = PrimitiveTopology::kTriangles;
+			Bool m_primitiveRestartEnabled = false;
+		} m_ia;
+
+		class Rast
+		{
+		public:
+			FillMode m_fillMode = FillMode::kSolid;
+			FaceSelectionBit m_cullMode = FaceSelectionBit::kBack;
+#if ANKI_GR_BACKEND_VULKAN
+			Bool m_depthBiasEnabled = false;
+#else
+			F32 m_depthBias = 0.0f;
+			F32 m_depthBiasClamp = 0.0f;
+			F32 m_slopeScaledDepthBias = 0.0f;
+#endif
+		} m_rast;
+
+		class
+		{
+		public:
+			class StencilFace
+			{
+			public:
+				StencilOperation m_fail = StencilOperation::kKeep;
+				StencilOperation m_stencilPassDepthFail = StencilOperation::kKeep;
+				StencilOperation m_stencilPassDepthPass = StencilOperation::kKeep;
+				CompareOperation m_compare = CompareOperation::kAlways;
+#if ANKI_GR_BACKEND_DIRECT3D
+				U32 m_compareMask = 0x5A5A5A5A; ///< Use a stupid number to initialize.
+				U32 m_writeMask = 0x5A5A5A5A; ///< Use a stupid number to initialize.
+#endif
+			};
+
+			Array<StencilFace, 2> m_face;
+		} m_stencil;
+
+		class
+		{
+		public:
+			CompareOperation m_compare = CompareOperation::kLess;
+			Bool m_writeEnabled = true;
+		} m_depth;
+
+		class
+		{
+		public:
+			class Rt
+			{
+			public:
+				ColorBit m_channelWriteMask = ColorBit::kAll;
+				BlendFactor m_srcRgb = BlendFactor::kOne;
+				BlendFactor m_dstRgb = BlendFactor::kZero;
+				BlendFactor m_srcA = BlendFactor::kOne;
+				BlendFactor m_dstA = BlendFactor::kZero;
+				BlendOperation m_funcRgb = BlendOperation::kAdd;
+				BlendOperation m_funcA = BlendOperation::kAdd;
+			};
+
+			Array<Rt, kMaxColorRenderTargets> m_colorRts;
+			Bool m_alphaToCoverage = false;
+		} m_blend;
+
+		class Misc
+		{
+		public:
+			Array<Format, kMaxColorRenderTargets> m_colorRtFormats = {};
+			Format m_depthStencilFormat = Format::kNone;
+			BitSet<kMaxColorRenderTargets> m_colorRtMask = {false};
+
+#if ANKI_GR_BACKEND_VULKAN
+			Bool m_rendersToSwapchain = false;
+			Bool m_pipelineStatisticsEnabled = false;
+#endif
+		} m_misc;
+
+		ShaderProgram* m_shaderProg = nullptr;
+	} m_staticState;
+	ANKI_END_PACKED_STRUCT
+
+	class DynamicState
+	{
+	public:
+		class StencilFace
+		{
+		public:
+#if ANKI_GR_BACKEND_VULKAN
+			U32 m_compareMask = 0x5A5A5A5A; ///< Use a stupid number to initialize.
+			U32 m_writeMask = 0x5A5A5A5A; ///< Use a stupid number to initialize.
+#endif
+			U32 m_ref = 0x5A5A5A5A; ///< Use a stupid number to initialize.
+		};
+
+		Array<StencilFace, 2> m_stencilFaces;
+
+		Array<U32, 4> m_viewport = {};
+		Array<U32, 4> m_scissor = {0, 0, kMaxU32, kMaxU32};
+
+#if ANKI_GR_BACKEND_VULKAN
+		F32 m_depthBiasConstantFactor = 0.0f;
+		F32 m_depthBiasClamp = 0.0f;
+		F32 m_depthBiasSlopeFactor = 0.0f;
+
+		F32 m_lineWidth = 1.0f;
+#endif
+
+#if ANKI_GR_BACKEND_VULKAN
+		Bool m_stencilCompareMaskDirty : 1 = true;
+		Bool m_stencilWriteMaskDirty : 1 = true;
+		Bool m_depthBiasDirty : 1 = true;
+		Bool m_lineWidthDirty : 1 = true;
+#else
+		Bool m_topologyDirty : 1 = true;
+#endif
+		Bool m_stencilRefDirty : 1 = true;
+		Bool m_viewportDirty : 1 = true;
+		Bool m_scissorDirty : 1 = true;
+	} m_dynState;
+
+	class Hashes
+	{
+	public:
+		U64 m_vert = 0;
+		U64 m_rast = 0;
+		U64 m_ia = 0;
+		U64 m_depthStencil = 0;
+		U64 m_blend = 0;
+		U64 m_misc = 0;
+		U64 m_shaderProg = 0;
+	} m_hashes;
+
+	U64 m_globalHash = 0;
+
+	UVec2 m_rtsSize = UVec2(0u);
+
+	Bool updateHashes();
+};
+/// @}
+
+} // end namespace anki

+ 1 - 0
AnKi/Gr/CMakeLists.txt

@@ -8,6 +8,7 @@ set(backend_sources
 	Utils/StackGpuMemoryPool.cpp
 	BackendCommon/Functions.cpp
 	BackendCommon/Common.cpp
+	BackendCommon/GraphicsStateTracker.cpp
 	Utils/SegregatedListsGpuMemoryPool.cpp)
 
 set(backend_headers

+ 0 - 3
AnKi/Gr/CommandBuffer.h

@@ -263,9 +263,6 @@ public:
 		setBlendOperation(attachment, func, func);
 	}
 
-	/// Set the rasterizatin order. By default it's RasterizationOrder::ORDERED.
-	void setRasterizationOrder(RasterizationOrder order);
-
 	/// Set the line width. By default it's undefined.
 	void setLineWidth(F32 lineWidth);
 

+ 2 - 2
AnKi/Gr/Common.cpp

@@ -170,8 +170,8 @@ Error ShaderReflection::linkShaderReflection(const ShaderReflection& a, const Sh
 
 	c.m_descriptor.m_hasVkBindlessDescriptorSet = a.m_descriptor.m_hasVkBindlessDescriptorSet || b.m_descriptor.m_hasVkBindlessDescriptorSet;
 
-	c.m_vertex.m_vertexAttributeLocations =
-		(a.m_vertex.m_vertexAttributeMask.getAnySet()) ? a.m_vertex.m_vertexAttributeLocations : b.m_vertex.m_vertexAttributeLocations;
+	c.m_vertex.m_vkVertexAttributeLocations =
+		(a.m_vertex.m_vertexAttributeMask.getAnySet()) ? a.m_vertex.m_vkVertexAttributeLocations : b.m_vertex.m_vkVertexAttributeLocations;
 	c.m_vertex.m_vertexAttributeMask = a.m_vertex.m_vertexAttributeMask | b.m_vertex.m_vertexAttributeMask;
 
 	c.m_fragment.m_colorAttachmentWritemask = a.m_fragment.m_colorAttachmentWritemask | b.m_fragment.m_colorAttachmentWritemask;

+ 3 - 11
AnKi/Gr/Common.h

@@ -794,14 +794,6 @@ inline U32 getIndexSize(IndexType type)
 	return 2u << U32(type);
 }
 
-/// Rasterization order.
-enum class RasterizationOrder : U8
-{
-	kOrdered,
-	kRelaxed,
-	kCount
-};
-
 /// Acceleration structure type.
 enum class AccelerationStructureType : U8
 {
@@ -1023,7 +1015,7 @@ public:
 	class
 	{
 	public:
-		Array<U8, U32(VertexAttributeSemantic::kCount)> m_vertexAttributeLocations;
+		Array<U8, U32(VertexAttributeSemantic::kCount)> m_vkVertexAttributeLocations;
 		BitSet<U32(VertexAttributeSemantic::kCount), U8> m_vertexAttributeMask = {false};
 	} m_vertex;
 
@@ -1037,7 +1029,7 @@ public:
 
 	ShaderReflection()
 	{
-		m_vertex.m_vertexAttributeLocations.fill(kMaxU8);
+		m_vertex.m_vkVertexAttributeLocations.fill(kMaxU8);
 	}
 
 	void validate() const
@@ -1045,7 +1037,7 @@ public:
 		m_descriptor.validate();
 		for(VertexAttributeSemantic semantic : EnumIterable<VertexAttributeSemantic>())
 		{
-			ANKI_ASSERT(!m_vertex.m_vertexAttributeMask.get(semantic) || m_vertex.m_vertexAttributeLocations[semantic] != kMaxU8);
+			ANKI_ASSERT(!m_vertex.m_vertexAttributeMask.get(semantic) || m_vertex.m_vkVertexAttributeLocations[semantic] != kMaxU8);
 		}
 
 		const U32 attachmentCount = m_fragment.m_colorAttachmentWritemask.getSetBitCount();

+ 2 - 7
AnKi/Gr/D3D/D3DCommandBuffer.cpp

@@ -309,7 +309,7 @@ void CommandBuffer::bindShaderProgram(ShaderProgram* prog)
 	}
 	else
 	{
-		self.m_graphicsState.bindShaderProgram(&progImpl);
+		self.m_graphicsState.bindShaderProgram(prog);
 	}
 
 	// Shader program means descriptors so bind the descriptor heaps
@@ -375,7 +375,7 @@ void CommandBuffer::beginRenderPass(ConstWeakArray<RenderTarget> colorRts, Rende
 		rtHeight = tex.getHeight() >> depthStencilRt->m_textureView.getFirstMipmap();
 	}
 
-	self.m_graphicsState.beginRenderPass(ConstWeakArray(colorRtFormats.getBegin(), colorRts.getSize()), dsFormat, rtWidth, rtHeight);
+	self.m_graphicsState.beginRenderPass(ConstWeakArray(colorRtFormats.getBegin(), colorRts.getSize()), dsFormat, UVec2(rtWidth, rtHeight));
 
 	self.m_cmdList->BeginRenderPass(colorRts.getSize(), colorRtDescs.getBegin(), (depthStencilRt) ? &dsDesc : nullptr,
 									D3D12_RENDER_PASS_FLAG_ALLOW_UAV_WRITES);
@@ -793,11 +793,6 @@ void CommandBuffer::setPushConstants(const void* data, U32 dataSize)
 	self.m_descriptors.setRootConstants(data, dataSize);
 }
 
-void CommandBuffer::setRasterizationOrder([[maybe_unused]] RasterizationOrder order)
-{
-	// Only a Vulkan/AMD thing, skip
-}
-
 void CommandBuffer::setLineWidth(F32 width)
 {
 	ANKI_D3D_SELF(CommandBufferImpl);

+ 2 - 1
AnKi/Gr/D3D/D3DCommandBuffer.h

@@ -10,6 +10,7 @@
 #include <AnKi/Gr/D3D/D3DDescriptor.h>
 #include <AnKi/Gr/D3D/D3DGraphicsState.h>
 #include <AnKi/Gr/D3D/D3DQueryFactory.h>
+#include <AnKi/Gr/D3D/D3DShaderProgram.h>
 
 namespace anki {
 
@@ -65,7 +66,7 @@ private:
 	{
 		commandCommon();
 
-		m_graphicsState.getShaderProgram().m_graphics.m_pipelineFactory->flushState(m_graphicsState, *m_cmdList);
+		static_cast<ShaderProgramImpl&>(m_graphicsState.getShaderProgram()).m_graphics.m_pipelineFactory->flushState(m_graphicsState, *m_cmdList);
 		m_descriptors.flush(*m_cmdList);
 	}
 

+ 64 - 173
AnKi/Gr/D3D/D3DGraphicsState.cpp

@@ -4,6 +4,7 @@
 // http://www.anki3d.org/LICENSE
 
 #include <AnKi/Gr/D3D/D3DGraphicsState.h>
+#include <AnKi/Gr/D3D/D3DShaderProgram.h>
 #include <AnKi/Gr/BackendCommon/Functions.h>
 
 namespace anki {
@@ -49,122 +50,6 @@ static void getVertexAttributeSemanticInfo(VertexAttributeSemantic x, const Char
 	};
 }
 
-Bool GraphicsStateTracker::updateHashes()
-{
-	if(m_hashes.m_vert == 0)
-	{
-		if(m_state.m_vert.m_activeAttribs.getAnySet())
-		{
-			m_hashes.m_vert = 0xC0FEE;
-
-			for(VertexAttributeSemantic i : EnumIterable<VertexAttributeSemantic>())
-			{
-				if(!m_state.m_vert.m_activeAttribs.get(i))
-				{
-					continue;
-				}
-
-				ANKI_ASSERT(m_state.m_vert.m_attribsSetMask.get(i) && "Forgot to set the vert attribute");
-				m_hashes.m_vert = appendObjectHash(m_state.m_vert.m_attribs[i], m_hashes.m_vert);
-
-				ANKI_ASSERT(m_state.m_vert.m_bindingsSetMask.get(m_state.m_vert.m_attribs[i].m_binding) && "Forgot to inform about the vert binding");
-				m_hashes.m_vert = appendObjectHash(m_state.m_vert.m_bindings[m_state.m_vert.m_attribs[i].m_binding], m_hashes.m_vert);
-			}
-		}
-		else
-		{
-			m_hashes.m_vert = 0xC0FEE;
-		}
-	}
-
-	if(m_hashes.m_rast == 0)
-	{
-		m_hashes.m_rast = computeObjectHash(&m_state.m_rast);
-	}
-
-	if(m_hashes.m_depthStencil == 0)
-	{
-		const Bool hasStencil =
-			m_state.m_misc.m_depthStencilFormat != Format::kNone && getFormatInfo(m_state.m_misc.m_depthStencilFormat).isStencil();
-
-		const Bool hasDepth = m_state.m_misc.m_depthStencilFormat != Format::kNone && getFormatInfo(m_state.m_misc.m_depthStencilFormat).isDepth();
-
-		m_hashes.m_depthStencil = 0xC0FEE;
-
-		if(hasStencil)
-		{
-			m_hashes.m_depthStencil = appendObjectHash(m_state.m_stencil, m_hashes.m_depthStencil);
-		}
-
-		if(hasDepth)
-		{
-			m_hashes.m_depthStencil = appendObjectHash(m_state.m_depth, m_hashes.m_depthStencil);
-		}
-	}
-
-	if(m_hashes.m_blend == 0)
-	{
-		if(m_state.m_misc.m_colorRtMask.getAnySet())
-		{
-			m_hashes.m_blend = m_state.m_blend.m_alphaToCoverage;
-
-			for(U32 i = 0; i < kMaxColorRenderTargets; ++i)
-			{
-				if(m_state.m_misc.m_colorRtMask.get(i))
-				{
-					m_hashes.m_blend = appendObjectHash(m_state.m_blend.m_colorRts[i], m_hashes.m_blend);
-				}
-			}
-		}
-		else
-		{
-			m_hashes.m_blend = 0xC0FFE;
-		}
-	}
-
-	if(m_hashes.m_misc == 0)
-	{
-		Array<U32, kMaxColorRenderTargets + 3> toHash;
-		U32 toHashCount = 0;
-
-		toHash[toHashCount++] = m_state.m_misc.m_colorRtMask.getData()[0];
-		for(U32 i = 0; i < kMaxColorRenderTargets; ++i)
-		{
-			if(m_state.m_misc.m_colorRtMask.get(i))
-			{
-				toHash[toHashCount++] = U32(m_state.m_misc.m_colorRtFormats[i]);
-			}
-		}
-
-		if(m_state.m_misc.m_depthStencilFormat != Format::kNone)
-		{
-			toHash[toHashCount++] = U32(m_state.m_misc.m_depthStencilFormat);
-		}
-
-		toHash[toHashCount++] = U32(m_state.m_misc.m_topology);
-
-		m_hashes.m_misc = computeHash(toHash.getBegin(), sizeof(toHash[0]) * toHashCount);
-	}
-
-	if(m_hashes.m_shaderProg == 0)
-	{
-		m_hashes.m_shaderProg = m_state.m_shaderProg->getUuid();
-	}
-
-	// Compute complete hash
-	const U64 globalHash = computeObjectHash(m_hashes);
-
-	if(globalHash != m_globalHash)
-	{
-		m_globalHash = globalHash;
-		return true;
-	}
-	else
-	{
-		return false;
-	}
-}
-
 GraphicsPipelineFactory::~GraphicsPipelineFactory()
 {
 	for(auto pso : m_map)
@@ -175,42 +60,51 @@ GraphicsPipelineFactory::~GraphicsPipelineFactory()
 
 void GraphicsPipelineFactory::flushState(GraphicsStateTracker& state, D3D12GraphicsCommandListX& cmdList)
 {
+	const GraphicsStateTracker::StaticState& staticState = state.m_staticState;
+	GraphicsStateTracker::DynamicState& dynState = state.m_dynState;
+
 	// Set dynamic state
-	if(state.m_dynState.m_stencilRefMaskDirty && state.m_state.m_misc.m_depthStencilFormat != Format::kNone
-	   && getFormatInfo(state.m_state.m_misc.m_depthStencilFormat).isStencil())
+	const auto& ss = staticState.m_stencil;
+	const Bool stencilTestEnabled = anki::stencilTestEnabled(ss.m_face[0].m_fail, ss.m_face[0].m_stencilPassDepthFail,
+															 ss.m_face[0].m_stencilPassDepthPass, ss.m_face[0].m_compare)
+									|| anki::stencilTestEnabled(ss.m_face[1].m_fail, ss.m_face[1].m_stencilPassDepthFail,
+																ss.m_face[1].m_stencilPassDepthPass, ss.m_face[1].m_compare);
+
+	const Bool hasStencilRt =
+		staticState.m_misc.m_depthStencilFormat != Format::kNone && getFormatInfo(staticState.m_misc.m_depthStencilFormat).isStencil();
+
+	if(stencilTestEnabled && hasStencilRt && dynState.m_stencilRefDirty)
 	{
-		state.m_dynState.m_stencilRefMaskDirty = false;
-		cmdList.OMSetStencilRef(state.m_dynState.m_stencilRefMask);
+		dynState.m_stencilRefDirty = false;
+		cmdList.OMSetStencilRef(dynState.m_stencilFaces[0].m_ref);
 	}
 
-	if(state.m_dynState.m_topologyDirty)
+	if(dynState.m_topologyDirty)
 	{
-		state.m_dynState.m_topologyDirty = false;
-		cmdList.IASetPrimitiveTopology(convertPrimitiveTopology2(state.m_dynState.m_topology));
+		dynState.m_topologyDirty = false;
+		cmdList.IASetPrimitiveTopology(convertPrimitiveTopology2(staticState.m_ia.m_topology));
 	}
 
-	if(state.m_dynState.m_viewportDirty)
+	if(dynState.m_viewportDirty)
 	{
-		state.m_dynState.m_viewportDirty = false;
-		const D3D12_VIEWPORT vp = {.TopLeftX = F32(state.m_dynState.m_viewport[0]),
-								   .TopLeftY = F32(state.m_dynState.m_viewport[1]),
-								   .Width = F32(state.m_dynState.m_viewport[2]),
-								   .Height = F32(state.m_dynState.m_viewport[3]),
+		dynState.m_viewportDirty = false;
+		const D3D12_VIEWPORT vp = {.TopLeftX = F32(dynState.m_viewport[0]),
+								   .TopLeftY = F32(dynState.m_viewport[1]),
+								   .Width = F32(dynState.m_viewport[2]),
+								   .Height = F32(dynState.m_viewport[3]),
 								   .MinDepth = 0.0f,
 								   .MaxDepth = 1.0f};
 		cmdList.RSSetViewports(1, &vp);
 	}
 
-	if(state.m_dynState.m_scissorDirty)
+	if(dynState.m_scissorDirty)
 	{
-		state.m_dynState.m_scissorDirty = false;
+		dynState.m_scissorDirty = false;
 
-		const U32 minx = max(state.m_dynState.m_scissor[0], state.m_dynState.m_viewport[0]);
-		const U32 miny = max(state.m_dynState.m_scissor[1], state.m_dynState.m_viewport[1]);
-		const U32 right =
-			min(state.m_dynState.m_scissor[0] + state.m_dynState.m_scissor[2], state.m_dynState.m_viewport[0] + state.m_dynState.m_viewport[2]);
-		const U32 bottom =
-			min(state.m_dynState.m_scissor[1] + state.m_dynState.m_scissor[3], state.m_dynState.m_viewport[1] + state.m_dynState.m_viewport[3]);
+		const U32 minx = max(dynState.m_scissor[0], dynState.m_viewport[0]);
+		const U32 miny = max(dynState.m_scissor[1], dynState.m_viewport[1]);
+		const U32 right = min(dynState.m_scissor[0] + dynState.m_scissor[2], dynState.m_viewport[0] + dynState.m_viewport[2]);
+		const U32 bottom = min(dynState.m_scissor[1] + dynState.m_scissor[3], dynState.m_viewport[1] + dynState.m_viewport[3]);
 
 		const D3D12_RECT rect = {.left = I32(minx), .top = I32(miny), .right = I32(right), .bottom = I32(bottom)};
 		cmdList.RSSetScissorRects(1, &rect);
@@ -242,22 +136,22 @@ void GraphicsPipelineFactory::flushState(GraphicsStateTracker& state, D3D12Graph
 
 	// PSO not found, proactively create it WITHOUT a lock (we dont't want to serialize pipeline creation)
 
-	const ShaderProgramImpl& prog = *state.m_state.m_shaderProg;
+	const ShaderProgramImpl& prog = static_cast<const ShaderProgramImpl&>(*staticState.m_shaderProg);
 
 	// Vertex input
 	Array<D3D12_INPUT_ELEMENT_DESC, U32(VertexAttributeSemantic::kCount)> inputElementDescs;
 	U32 inputElementDescCount = 0;
 	for(VertexAttributeSemantic i : EnumIterable<VertexAttributeSemantic>())
 	{
-		if(state.m_state.m_vert.m_activeAttribs.get(i))
+		if(staticState.m_vert.m_activeAttribs.get(i))
 		{
 			D3D12_INPUT_ELEMENT_DESC& elem = inputElementDescs[inputElementDescCount++];
 
 			getVertexAttributeSemanticInfo(i, elem.SemanticName, elem.SemanticIndex);
-			elem.Format = DXGI_FORMAT(state.m_state.m_vert.m_attribs[i].m_fmt);
-			elem.InputSlot = state.m_state.m_vert.m_attribs[i].m_binding;
-			elem.AlignedByteOffset = state.m_state.m_vert.m_attribs[i].m_relativeOffset;
-			elem.InputSlotClass = (state.m_state.m_vert.m_bindings[state.m_state.m_vert.m_attribs[i].m_binding].m_stepRate == VertexStepRate::kVertex)
+			elem.Format = DXGI_FORMAT(staticState.m_vert.m_attribs[i].m_fmt);
+			elem.InputSlot = staticState.m_vert.m_attribs[i].m_binding;
+			elem.AlignedByteOffset = staticState.m_vert.m_attribs[i].m_relativeOffset;
+			elem.InputSlotClass = (staticState.m_vert.m_bindings[staticState.m_vert.m_attribs[i].m_binding].m_stepRate == VertexStepRate::kVertex)
 									  ? D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA
 									  : D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA;
 			elem.InstanceDataStepRate = (elem.InputSlotClass == D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA) ? 0 : 1;
@@ -265,14 +159,14 @@ void GraphicsPipelineFactory::flushState(GraphicsStateTracker& state, D3D12Graph
 	}
 
 	// Blending
-	D3D12_BLEND_DESC blendDesc = {.AlphaToCoverageEnable = state.m_state.m_blend.m_alphaToCoverage, .IndependentBlendEnable = true};
+	D3D12_BLEND_DESC blendDesc = {.AlphaToCoverageEnable = staticState.m_blend.m_alphaToCoverage, .IndependentBlendEnable = true};
 	for(U32 i = 0; i < kMaxColorRenderTargets; ++i)
 	{
-		if(state.m_state.m_misc.m_colorRtMask.get(i))
+		if(staticState.m_misc.m_colorRtMask.get(i))
 		{
-			const auto& in = state.m_state.m_blend.m_colorRts[i];
+			const auto& in = staticState.m_blend.m_colorRts[i];
 			D3D12_RENDER_TARGET_BLEND_DESC& out = blendDesc.RenderTarget[i];
-			out.BlendEnable = blendingDisabled(in.m_srcRgb, in.m_dstRgb, in.m_srcA, in.m_dstA, in.m_funcRgb, in.m_funcA);
+			out.BlendEnable = blendingEnabled(in.m_srcRgb, in.m_dstRgb, in.m_srcA, in.m_dstA, in.m_funcRgb, in.m_funcA);
 			out.SrcBlend = convertBlendFactor(in.m_srcRgb);
 			out.DestBlend = convertBlendFactor(in.m_dstRgb);
 			out.BlendOp = convertBlendOperation(in.m_funcRgb);
@@ -285,48 +179,45 @@ void GraphicsPipelineFactory::flushState(GraphicsStateTracker& state, D3D12Graph
 
 	// DS
 	D3D12_DEPTH_STENCIL_DESC2 dsDesc = {};
-	if(state.m_state.m_misc.m_depthStencilFormat != Format::kNone)
+	if(staticState.m_misc.m_depthStencilFormat != Format::kNone)
 	{
-		const Bool stencilEnabled = !stencilTestDisabled(state.m_state.m_stencil.m_fail[0], state.m_state.m_stencil.m_stencilPassDepthFail[0],
-														 state.m_state.m_stencil.m_stencilPassDepthPass[0], state.m_state.m_stencil.m_compare[0])
-									|| !stencilTestDisabled(state.m_state.m_stencil.m_fail[1], state.m_state.m_stencil.m_stencilPassDepthFail[1],
-															state.m_state.m_stencil.m_stencilPassDepthPass[1], state.m_state.m_stencil.m_compare[1]);
-
 		Array<D3D12_DEPTH_STENCILOP_DESC1, 2> stencilDescs;
 		for(U32 w = 0; w < 2; ++w)
 		{
-			stencilDescs[w].StencilFailOp = convertStencilOperation(state.m_state.m_stencil.m_fail[w]);
-			stencilDescs[w].StencilDepthFailOp = convertStencilOperation(state.m_state.m_stencil.m_stencilPassDepthFail[w]);
-			stencilDescs[w].StencilPassOp = convertStencilOperation(state.m_state.m_stencil.m_stencilPassDepthPass[w]);
-			stencilDescs[w].StencilFunc = convertComparisonFunc(state.m_state.m_stencil.m_compare[w]);
-			stencilDescs[w].StencilReadMask = U8(state.m_state.m_stencil.m_compareMask[w]);
-			stencilDescs[w].StencilWriteMask = U8(state.m_state.m_stencil.m_writeMask[w]);
+			stencilDescs[w].StencilFailOp = convertStencilOperation(staticState.m_stencil.m_face[w].m_fail);
+			stencilDescs[w].StencilDepthFailOp = convertStencilOperation(staticState.m_stencil.m_face[w].m_stencilPassDepthFail);
+			stencilDescs[w].StencilPassOp = convertStencilOperation(staticState.m_stencil.m_face[w].m_stencilPassDepthPass);
+			stencilDescs[w].StencilFunc = convertComparisonFunc(staticState.m_stencil.m_face[w].m_compare);
+
+			ANKI_ASSERT(staticState.m_stencil.m_face[w].m_compareMask != 0x5A5A5A5A && staticState.m_stencil.m_face[w].m_writeMask != 0x5A5A5A5A);
+			stencilDescs[w].StencilReadMask = U8(staticState.m_stencil.m_face[w].m_compareMask);
+			stencilDescs[w].StencilWriteMask = U8(staticState.m_stencil.m_face[w].m_writeMask);
 		}
 
-		dsDesc = {.DepthEnable = state.m_state.m_depth.m_compare != CompareOperation::kAlways || state.m_state.m_depth.m_writeEnabled,
-				  .DepthWriteMask = state.m_state.m_depth.m_writeEnabled ? D3D12_DEPTH_WRITE_MASK_ALL : D3D12_DEPTH_WRITE_MASK_ZERO,
-				  .DepthFunc = convertCompareOperation(state.m_state.m_depth.m_compare),
-				  .StencilEnable = stencilEnabled,
+		dsDesc = {.DepthEnable = depthTestEnabled(staticState.m_depth.m_compare, staticState.m_depth.m_writeEnabled),
+				  .DepthWriteMask = staticState.m_depth.m_writeEnabled ? D3D12_DEPTH_WRITE_MASK_ALL : D3D12_DEPTH_WRITE_MASK_ZERO,
+				  .DepthFunc = convertCompareOperation(staticState.m_depth.m_compare),
+				  .StencilEnable = stencilTestEnabled,
 				  .FrontFace = stencilDescs[0],
 				  .BackFace = stencilDescs[1],
 				  .DepthBoundsTestEnable = false};
 	}
 
 	// Rast state
-	const D3D12_RASTERIZER_DESC2 rastDesc = {.FillMode = convertFillMode(state.m_state.m_rast.m_fillMode),
-											 .CullMode = convertCullMode(state.m_state.m_rast.m_cullMode),
+	const D3D12_RASTERIZER_DESC2 rastDesc = {.FillMode = convertFillMode(staticState.m_rast.m_fillMode),
+											 .CullMode = convertCullMode(staticState.m_rast.m_cullMode),
 											 .FrontCounterClockwise = true,
-											 .DepthBias = state.m_state.m_rast.m_depthBias,
-											 .DepthBiasClamp = state.m_state.m_rast.m_depthBiasClamp,
-											 .SlopeScaledDepthBias = state.m_state.m_rast.m_slopeScaledDepthBias};
+											 .DepthBias = staticState.m_rast.m_depthBias,
+											 .DepthBiasClamp = staticState.m_rast.m_depthBiasClamp,
+											 .SlopeScaledDepthBias = staticState.m_rast.m_slopeScaledDepthBias};
 
 	// Misc
 	D3D12_RT_FORMAT_ARRAY rtFormats = {};
 	for(U32 i = 0; i < kMaxColorRenderTargets; ++i)
 	{
-		if(state.m_state.m_misc.m_colorRtMask.get(i))
+		if(staticState.m_misc.m_colorRtMask.get(i))
 		{
-			rtFormats.RTFormats[i] = DXGI_FORMAT(state.m_state.m_misc.m_colorRtFormats[i]);
+			rtFormats.RTFormats[i] = DXGI_FORMAT(staticState.m_misc.m_colorRtFormats[i]);
 			rtFormats.NumRenderTargets = i + 1;
 		}
 	}
@@ -342,9 +233,9 @@ void GraphicsPipelineFactory::flushState(GraphicsStateTracker& state, D3D12Graph
 
 	CD3DX12_PIPELINE_STATE_STREAM5 desc = {};
 	desc.Flags = D3D12_PIPELINE_STATE_FLAG_DYNAMIC_DEPTH_BIAS;
-	desc.pRootSignature = &state.m_state.m_shaderProg->m_rootSignature->getD3DRootSignature();
+	desc.pRootSignature = &prog.m_rootSignature->getD3DRootSignature();
 	desc.InputLayout = D3D12_INPUT_LAYOUT_DESC{.pInputElementDescs = inputElementDescs.getBegin(), .NumElements = inputElementDescCount};
-	desc.PrimitiveTopologyType = convertPrimitiveTopology(state.m_state.m_misc.m_topology);
+	desc.PrimitiveTopologyType = convertPrimitiveTopology(staticState.m_ia.m_topology);
 	ANKI_SET_IR(VS, kVertex)
 	ANKI_SET_IR(GS, kGeometry)
 	ANKI_SET_IR(HS, kTessellationControl)
@@ -354,7 +245,7 @@ void GraphicsPipelineFactory::flushState(GraphicsStateTracker& state, D3D12Graph
 	ANKI_SET_IR(MS, kMesh)
 	desc.BlendState = CD3DX12_BLEND_DESC(blendDesc);
 	desc.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC2(dsDesc);
-	desc.DSVFormat = DXGI_FORMAT(state.m_state.m_misc.m_depthStencilFormat);
+	desc.DSVFormat = DXGI_FORMAT(staticState.m_misc.m_depthStencilFormat);
 	desc.RasterizerState = CD3DX12_RASTERIZER_DESC2(rastDesc);
 	desc.RTVFormats = rtFormats;
 	desc.SampleDesc = sampleDesc;

+ 1 - 421
AnKi/Gr/D3D/D3DGraphicsState.h

@@ -4,7 +4,7 @@
 // http://www.anki3d.org/LICENSE
 
 #include <AnKi/Gr/D3D/D3DCommon.h>
-#include <AnKi/Gr/D3D/D3DShaderProgram.h>
+#include <AnKi/Gr/BackendCommon/GraphicsStateTracker.h>
 #include <AnKi/Util/WeakArray.h>
 #include <AnKi/Util/HashMap.h>
 
@@ -13,426 +13,6 @@ namespace anki {
 /// @addtogroup d3d
 /// @{
 
-/// This is the way the command buffer sets the graphics state.
-class GraphicsStateTracker
-{
-	friend class GraphicsPipelineFactory;
-
-public:
-	void bindVertexBuffer(U32 binding, VertexStepRate stepRate)
-	{
-		if(!m_state.m_vert.m_bindingsSetMask.get(binding) || m_state.m_vert.m_bindings[binding].m_stepRate != stepRate)
-		{
-			m_state.m_vert.m_bindingsSetMask.set(binding);
-			m_state.m_vert.m_bindings[binding].m_stepRate = stepRate;
-			m_hashes.m_vert = 0;
-		}
-	}
-
-	void setVertexAttribute(VertexAttributeSemantic attribute, U32 buffBinding, Format fmt, U32 relativeOffset)
-	{
-		auto& attr = m_state.m_vert.m_attribs[attribute];
-		if(!m_state.m_vert.m_attribsSetMask.get(attribute) || attr.m_fmt != fmt || attr.m_binding != buffBinding
-		   || attr.m_relativeOffset != relativeOffset)
-		{
-			attr.m_fmt = fmt;
-			attr.m_binding = buffBinding;
-			attr.m_relativeOffset = relativeOffset;
-			m_state.m_vert.m_attribsSetMask.set(attribute);
-			m_hashes.m_vert = 0;
-		}
-	}
-
-	void setFillMode(FillMode mode)
-	{
-		ANKI_ASSERT(mode < FillMode::kCount);
-		if(m_state.m_rast.m_fillMode != mode)
-		{
-			m_state.m_rast.m_fillMode = mode;
-			m_hashes.m_rast = 0;
-		}
-	}
-
-	void setCullMode(FaceSelectionBit mode)
-	{
-		if(m_state.m_rast.m_cullMode != mode)
-		{
-			m_state.m_rast.m_cullMode = mode;
-			m_hashes.m_rast = 0;
-		}
-	}
-
-	void setStencilOperations(FaceSelectionBit face, StencilOperation stencilFail, StencilOperation stencilPassDepthFail,
-							  StencilOperation stencilPassDepthPass)
-	{
-		ANKI_ASSERT(face != FaceSelectionBit::kNone);
-		if(!!(face & FaceSelectionBit::kFront)
-		   && (m_state.m_stencil.m_fail[0] != stencilFail || m_state.m_stencil.m_stencilPassDepthFail[0] != stencilPassDepthFail
-			   || m_state.m_stencil.m_stencilPassDepthPass[0] != stencilPassDepthPass))
-		{
-			m_state.m_stencil.m_fail[0] = stencilFail;
-			m_state.m_stencil.m_stencilPassDepthFail[0] = stencilPassDepthFail;
-			m_state.m_stencil.m_stencilPassDepthPass[0] = stencilPassDepthPass;
-			m_hashes.m_depthStencil = 0;
-		}
-
-		if(!!(face & FaceSelectionBit::kBack)
-		   && (m_state.m_stencil.m_fail[1] != stencilFail || m_state.m_stencil.m_stencilPassDepthFail[1] != stencilPassDepthFail
-			   || m_state.m_stencil.m_stencilPassDepthPass[1] != stencilPassDepthPass))
-		{
-			m_state.m_stencil.m_fail[1] = stencilFail;
-			m_state.m_stencil.m_stencilPassDepthFail[1] = stencilPassDepthFail;
-			m_state.m_stencil.m_stencilPassDepthPass[1] = stencilPassDepthPass;
-			m_hashes.m_depthStencil = 0;
-		}
-	}
-
-	void setStencilCompareOperation(FaceSelectionBit face, CompareOperation comp)
-	{
-		ANKI_ASSERT(face != FaceSelectionBit::kNone);
-		if(!!(face & FaceSelectionBit::kFront) && m_state.m_stencil.m_compare[0] != comp)
-		{
-			m_state.m_stencil.m_compare[0] = comp;
-			m_hashes.m_depthStencil = 0;
-		}
-
-		if(!!(face & FaceSelectionBit::kBack) && m_state.m_stencil.m_compare[1] != comp)
-		{
-			m_state.m_stencil.m_compare[1] = comp;
-			m_hashes.m_depthStencil = 0;
-		}
-	}
-
-	void setStencilCompareMask(FaceSelectionBit face, U32 mask)
-	{
-		ANKI_ASSERT(face != FaceSelectionBit::kNone);
-		if(!!(face & FaceSelectionBit::kFront) && m_state.m_stencil.m_compareMask[0] != mask)
-		{
-			m_state.m_stencil.m_compareMask[0] = mask;
-			m_hashes.m_depthStencil = 0;
-		}
-
-		if(!!(face & FaceSelectionBit::kBack) && m_state.m_stencil.m_compareMask[1] != mask)
-		{
-			m_state.m_stencil.m_compareMask[1] = mask;
-			m_hashes.m_depthStencil = 0;
-		}
-	}
-
-	void setStencilWriteMask(FaceSelectionBit face, U32 mask)
-	{
-		ANKI_ASSERT(face != FaceSelectionBit::kNone);
-		if(!!(face & FaceSelectionBit::kFront) && m_state.m_stencil.m_writeMask[0] != mask)
-		{
-			m_state.m_stencil.m_writeMask[0] = mask;
-			m_hashes.m_depthStencil = 0;
-		}
-
-		if(!!(face & FaceSelectionBit::kBack) && m_state.m_stencil.m_writeMask[1] != mask)
-		{
-			m_state.m_stencil.m_writeMask[1] = mask;
-			m_hashes.m_depthStencil = 0;
-		}
-	}
-
-	void setStencilReference([[maybe_unused]] FaceSelectionBit face, U32 ref)
-	{
-		ANKI_ASSERT(face == FaceSelectionBit::kFrontAndBack && "D3D only supports a single value for both sides");
-		m_dynState.m_stencilRefMask = ref;
-		m_dynState.m_stencilRefMaskDirty = true;
-	}
-
-	void setDepthWrite(Bool enable)
-	{
-		if(m_state.m_depth.m_writeEnabled != enable)
-		{
-			m_state.m_depth.m_writeEnabled = enable;
-			m_hashes.m_depthStencil = 0;
-		}
-	}
-
-	void setDepthCompareOperation(CompareOperation op)
-	{
-		ANKI_ASSERT(op < CompareOperation::kCount);
-		if(m_state.m_depth.m_compare != op)
-		{
-			m_state.m_depth.m_compare = op;
-			m_hashes.m_depthStencil = 0;
-		}
-	}
-
-	void setPolygonOffset(F32 factor, F32 units)
-	{
-		if(m_state.m_rast.m_depthBias != factor || m_state.m_rast.m_slopeScaledDepthBias != factor)
-		{
-			m_state.m_rast.m_depthBias = factor;
-			m_state.m_rast.m_slopeScaledDepthBias = units;
-			m_state.m_rast.m_depthBiasClamp = 0.0f;
-			m_hashes.m_rast = 0;
-		}
-	}
-
-	void setAlphaToCoverage(Bool enable)
-	{
-		if(m_state.m_blend.m_alphaToCoverage != enable)
-		{
-			m_state.m_blend.m_alphaToCoverage = enable;
-			m_hashes.m_blend = 0;
-		}
-	}
-
-	void setColorChannelWriteMask(U32 attachment, ColorBit mask)
-	{
-		if(m_state.m_blend.m_colorRts[attachment].m_colorWritemask != mask)
-		{
-			m_state.m_blend.m_colorRts[attachment].m_colorWritemask = mask;
-			m_hashes.m_blend = 0;
-		}
-	}
-
-	void setBlendFactors(U32 attachment, BlendFactor srcRgb, BlendFactor dstRgb, BlendFactor srcA, BlendFactor dstA)
-	{
-		auto& rt = m_state.m_blend.m_colorRts[attachment];
-		if(rt.m_srcRgb != srcRgb || rt.m_srcA != srcA || rt.m_dstRgb != dstRgb || rt.m_dstA != dstA)
-		{
-			rt.m_srcRgb = srcRgb;
-			rt.m_srcA = srcA;
-			rt.m_dstRgb = dstRgb;
-			rt.m_dstA = dstA;
-			m_hashes.m_blend = 0;
-		}
-	}
-
-	void setBlendOperation(U32 attachment, BlendOperation funcRgb, BlendOperation funcA)
-	{
-		auto& rt = m_state.m_blend.m_colorRts[attachment];
-		if(rt.m_funcRgb != funcRgb || rt.m_funcA != funcA)
-		{
-			rt.m_funcRgb = funcRgb;
-			rt.m_funcA = funcA;
-			m_hashes.m_blend = 0;
-		}
-	}
-
-	void beginRenderPass(ConstWeakArray<Format> colorFormats, Format depthStencilFormat, U32 rtsWidth, U32 rtsHeight)
-	{
-		m_state.m_misc.m_colorRtMask.unsetAll();
-		for(U8 i = 0; i < colorFormats.getSize(); ++i)
-		{
-			ANKI_ASSERT(colorFormats[i] != Format::kNone);
-			m_state.m_misc.m_colorRtFormats[i] = colorFormats[i];
-			m_state.m_misc.m_colorRtMask.set(i);
-		}
-		m_state.m_misc.m_depthStencilFormat = depthStencilFormat;
-		m_hashes.m_misc = 0; // Always mark it dirty because calling beginRenderPass is a rare occurance and we want to avoid extra checks
-
-		m_rtsWidth = rtsWidth;
-		m_rtsHeight = rtsHeight;
-
-		// Scissor needs to be re-adjusted if it's in its default size
-		m_dynState.m_scissorDirty = true;
-	}
-
-	void bindShaderProgram(const ShaderProgramImpl* prog)
-	{
-		ANKI_ASSERT(prog);
-		const ShaderReflection& refl = prog->m_refl;
-		if(m_state.m_vert.m_activeAttribs != refl.m_vertex.m_vertexAttributeMask)
-		{
-			m_state.m_vert.m_activeAttribs = refl.m_vertex.m_vertexAttributeMask;
-			m_hashes.m_vert = 0;
-		}
-
-		if(m_state.m_misc.m_colorRtMask != refl.m_fragment.m_colorAttachmentWritemask)
-		{
-			m_state.m_misc.m_colorRtMask = refl.m_fragment.m_colorAttachmentWritemask;
-			m_hashes.m_misc = 0;
-		}
-
-		if(m_state.m_shaderProg != prog)
-		{
-			m_state.m_shaderProg = prog;
-			m_hashes.m_shaderProg = 0;
-		}
-	}
-
-	void setPrimitiveTopology(PrimitiveTopology topology)
-	{
-		if(m_state.m_misc.m_topology != topology)
-		{
-			m_state.m_misc.m_topology = topology;
-			m_hashes.m_misc = 0;
-			m_dynState.m_topology = topology;
-			m_dynState.m_topologyDirty = true;
-		}
-	}
-
-	void setViewport(U32 minx, U32 miny, U32 width, U32 height)
-	{
-		if(m_dynState.m_viewport[0] != minx || m_dynState.m_viewport[1] != miny || m_dynState.m_viewport[2] != width
-		   || m_dynState.m_viewport[3] != height)
-		{
-			m_dynState.m_viewport[0] = minx;
-			m_dynState.m_viewport[1] = miny;
-			m_dynState.m_viewport[2] = width;
-			m_dynState.m_viewport[3] = height;
-			m_dynState.m_viewportDirty = true;
-		}
-	}
-
-	void setScissor(U32 minx, U32 miny, U32 width, U32 height)
-	{
-		if(m_dynState.m_scissor[0] != minx || m_dynState.m_scissor[1] != miny || m_dynState.m_scissor[2] != width
-		   || m_dynState.m_scissor[3] != height)
-		{
-			m_dynState.m_scissor[0] = minx;
-			m_dynState.m_scissor[1] = miny;
-			m_dynState.m_scissor[2] = width;
-			m_dynState.m_scissor[3] = height;
-			m_dynState.m_scissorDirty = true;
-		}
-	}
-
-	const ShaderProgramImpl& getShaderProgram() const
-	{
-		ANKI_ASSERT(m_state.m_shaderProg);
-		return *m_state.m_shaderProg;
-	}
-
-private:
-	// The state vector
-	ANKI_BEGIN_PACKED_STRUCT
-	class State
-	{
-	public:
-		class
-		{
-		public:
-			class VertBinding
-			{
-			public:
-				VertexStepRate m_stepRate;
-
-				VertBinding()
-				{
-					// No init for opt
-				}
-			};
-
-			class VertAttrib
-			{
-			public:
-				U32 m_relativeOffset;
-				Format m_fmt;
-				U32 m_binding;
-
-				VertAttrib()
-				{
-					// No init for opt
-				}
-			};
-
-			Array<VertBinding, U32(VertexAttributeSemantic::kCount)> m_bindings;
-			Array<VertAttrib, U32(VertexAttributeSemantic::kCount)> m_attribs;
-			BitSet<U32(VertexAttributeSemantic::kCount)> m_activeAttribs = false;
-
-			BitSet<U32(VertexAttributeSemantic::kCount)> m_bindingsSetMask = false;
-			BitSet<U32(VertexAttributeSemantic::kCount)> m_attribsSetMask = false;
-		} m_vert;
-
-		class
-		{
-		public:
-			FillMode m_fillMode = FillMode::kSolid;
-			FaceSelectionBit m_cullMode = FaceSelectionBit::kBack;
-			F32 m_depthBias = 0.0f;
-			F32 m_depthBiasClamp = 0.0f;
-			F32 m_slopeScaledDepthBias = 0.0f;
-		} m_rast;
-
-		class
-		{
-		public:
-			Array<StencilOperation, 2> m_fail = {StencilOperation::kKeep, StencilOperation::kKeep};
-			Array<StencilOperation, 2> m_stencilPassDepthFail = {StencilOperation::kKeep, StencilOperation::kKeep};
-			Array<StencilOperation, 2> m_stencilPassDepthPass = {StencilOperation::kKeep, StencilOperation::kKeep};
-			Array<CompareOperation, 2> m_compare = {CompareOperation::kAlways, CompareOperation::kAlways};
-			Array<U32, 2> m_compareMask = {0x5A5A5A5A, 0x5A5A5A5A}; ///< Use a stupid number to initialize.
-			Array<U32, 2> m_writeMask = {0x5A5A5A5A, 0x5A5A5A5A}; ///< Use a stupid number to initialize.
-		} m_stencil;
-
-		class
-		{
-		public:
-			CompareOperation m_compare = CompareOperation::kLess;
-			Bool m_writeEnabled = true;
-		} m_depth;
-
-		class
-		{
-		public:
-			class Rt
-			{
-			public:
-				ColorBit m_colorWritemask = ColorBit::kAll;
-				BlendFactor m_srcRgb = BlendFactor::kOne;
-				BlendFactor m_dstRgb = BlendFactor::kOne;
-				BlendFactor m_srcA = BlendFactor::kZero;
-				BlendFactor m_dstA = BlendFactor::kZero;
-				BlendOperation m_funcRgb = BlendOperation::kAdd;
-				BlendOperation m_funcA = BlendOperation::kAdd;
-			};
-
-			Array<Rt, kMaxColorRenderTargets> m_colorRts;
-			Bool m_alphaToCoverage = false;
-		} m_blend;
-
-		class
-		{
-		public:
-			Array<Format, kMaxColorRenderTargets> m_colorRtFormats = {};
-			Format m_depthStencilFormat = Format::kNone;
-			BitSet<kMaxColorRenderTargets> m_colorRtMask = {false};
-			PrimitiveTopology m_topology = PrimitiveTopology::kTriangles;
-		} m_misc;
-
-		const ShaderProgramImpl* m_shaderProg = nullptr;
-	} m_state;
-	ANKI_END_PACKED_STRUCT
-
-	class DynamicState
-	{
-	public:
-		U32 m_stencilRefMask = 0x5A5A5A5A; ///< Use a stupid number to initialize.
-		PrimitiveTopology m_topology = PrimitiveTopology::kTriangles; ///< Yes, it's both dynamic and static state
-
-		Array<U32, 4> m_viewport = {};
-		Array<U32, 4> m_scissor = {0, 0, kMaxU32, kMaxU32};
-
-		Bool m_stencilRefMaskDirty : 1 = true;
-		Bool m_topologyDirty : 1 = true;
-		Bool m_viewportDirty : 1 = true;
-		Bool m_scissorDirty : 1 = true;
-	} m_dynState;
-
-	class Hashes
-	{
-	public:
-		U64 m_vert = 0;
-		U64 m_rast = 0;
-		U64 m_depthStencil = 0;
-		U64 m_blend = 0;
-		U64 m_misc = 0;
-		U64 m_shaderProg = 0;
-	} m_hashes;
-
-	U64 m_globalHash = 0;
-	U32 m_rtsWidth = 0;
-	U32 m_rtsHeight = 0;
-
-	Bool updateHashes();
-};
-
 class GraphicsPipelineFactory
 {
 public:

+ 0 - 5
AnKi/Gr/D3D/D3DShaderProgram.cpp

@@ -137,11 +137,6 @@ Error ShaderProgramImpl::init(const ShaderProgramInitInfo& inf)
 		const U32 size = s->getShaderBinarySize();
 
 		m_shaderBinarySizes[type] = size;
-
-		if(type == ShaderType::kFragment)
-		{
-			m_hasDiscard = s->hasDiscard();
-		}
 	}
 
 	// Misc

+ 0 - 2
AnKi/Gr/D3D/D3DShaderProgram.h

@@ -22,8 +22,6 @@ class ShaderProgramImpl final : public ShaderProgram
 public:
 	RootSignature* m_rootSignature = nullptr;
 
-	ShaderReflection m_refl;
-
 	class
 	{
 	public:

+ 0 - 9
AnKi/Gr/D3D/D3DTexture.cpp

@@ -657,11 +657,6 @@ void TextureImpl::computeBarrierInfo(TextureUsageBit usage, D3D12_BARRIER_SYNC&
 		accesses |= D3D12_BARRIER_ACCESS_SHADING_RATE_SOURCE;
 	}
 
-	if(!!(usage & TextureUsageBit::kGenerateMipmaps))
-	{
-		ANKI_ASSERT(!"TODO rm");
-	}
-
 	if(!!(usage & TextureUsageBit::kTransferDestination))
 	{
 		stages |= D3D12_BARRIER_SYNC_COPY;
@@ -728,10 +723,6 @@ D3D12_BARRIER_LAYOUT TextureImpl::computeLayout(TextureUsageBit usage) const
 		// SRV
 		out = D3D12_BARRIER_LAYOUT_SHADER_RESOURCE;
 	}
-	else if(usage == TextureUsageBit::kGenerateMipmaps)
-	{
-		ANKI_ASSERT(!"TODO rm");
-	}
 	else if(usage == TextureUsageBit::kTransferDestination)
 	{
 		out = D3D12_BARRIER_LAYOUT_COPY_DEST;

+ 7 - 2
AnKi/Gr/ShaderProgram.h

@@ -90,7 +90,12 @@ public:
 	Bool hasDiscard() const
 	{
 		ANKI_ASSERT(!!(m_shaderTypes & ShaderTypeBit::kFragment));
-		return m_hasDiscard;
+		return m_refl.m_fragment.m_discards;
+	}
+
+	const ShaderReflection& getReflection() const
+	{
+		return m_refl;
 	}
 
 protected:
@@ -98,7 +103,7 @@ protected:
 
 	ShaderTypeBit m_shaderTypes = ShaderTypeBit::kNone;
 
-	Bool m_hasDiscard = false;
+	ShaderReflection m_refl;
 
 	/// Construct.
 	ShaderProgram(CString name)

+ 2 - 0
AnKi/Gr/Vulkan/VkAccelerationStructure.cpp

@@ -307,6 +307,8 @@ VkMemoryBarrier AccelerationStructureImpl::computeBarrierInfo(AccelerationStruct
 
 	srcStages_ |= srcStages;
 	dstStages_ |= dstStages;
+
+	return barrier;
 }
 
 } // end namespace anki

+ 35 - 195
AnKi/Gr/Vulkan/VkCommandBuffer.cpp

@@ -12,6 +12,7 @@
 #include <AnKi/Gr/Vulkan/VkTimestampQuery.h>
 #include <AnKi/Gr/Vulkan/VkSampler.h>
 #include <AnKi/Gr/Vulkan/VkAccelerationStructure.h>
+#include <AnKi/Gr/Vulkan/VkShaderProgram.h>
 
 #if ANKI_DLSS
 #	include <ThirdParty/DlssSdk/sdk/include/nvsdk_ngx.h>
@@ -47,7 +48,8 @@ void CommandBuffer::bindVertexBuffer(U32 binding, const BufferView& buff, U32 st
 
 	ANKI_VK_SELF(CommandBufferImpl);
 	self.commandCommon();
-	self.m_state.bindVertexBuffer(binding, stride, stepRate);
+	self.m_graphicsState.bindVertexBuffer(binding, stepRate, stride);
+
 	const VkBuffer vkbuff = static_cast<const BufferImpl&>(buff.getBuffer()).getHandle();
 	vkCmdBindVertexBuffers(self.m_handle, binding, 1, &vkbuff, &buff.getOffset());
 }
@@ -56,7 +58,7 @@ void CommandBuffer::setVertexAttribute(VertexAttributeSemantic attribute, U32 bu
 {
 	ANKI_VK_SELF(CommandBufferImpl);
 	self.commandCommon();
-	self.m_state.setVertexAttribute(attribute, buffBinding, fmt, relativeOffset);
+	self.m_graphicsState.setVertexAttribute(attribute, buffBinding, fmt, relativeOffset);
 }
 
 void CommandBuffer::bindIndexBuffer(const BufferView& buff, IndexType type)
@@ -74,7 +76,7 @@ void CommandBuffer::setPrimitiveRestart(Bool enable)
 {
 	ANKI_VK_SELF(CommandBufferImpl);
 	self.commandCommon();
-	self.m_state.setPrimitiveRestart(enable);
+	self.m_graphicsState.setPrimitiveRestart(enable);
 }
 
 void CommandBuffer::setViewport(U32 minx, U32 miny, U32 width, U32 height)
@@ -82,16 +84,7 @@ void CommandBuffer::setViewport(U32 minx, U32 miny, U32 width, U32 height)
 	ANKI_VK_SELF(CommandBufferImpl);
 	ANKI_ASSERT(width > 0 && height > 0);
 	self.commandCommon();
-
-	if(self.m_viewport[0] != minx || self.m_viewport[1] != miny || self.m_viewport[2] != width || self.m_viewport[3] != height)
-	{
-		self.m_viewportDirty = true;
-
-		self.m_viewport[0] = minx;
-		self.m_viewport[1] = miny;
-		self.m_viewport[2] = width;
-		self.m_viewport[3] = height;
-	}
+	self.m_graphicsState.setViewport(minx, miny, width, height);
 }
 
 void CommandBuffer::setScissor(U32 minx, U32 miny, U32 width, U32 height)
@@ -99,37 +92,28 @@ void CommandBuffer::setScissor(U32 minx, U32 miny, U32 width, U32 height)
 	ANKI_VK_SELF(CommandBufferImpl);
 	ANKI_ASSERT(width > 0 && height > 0);
 	self.commandCommon();
-
-	if(self.m_scissor[0] != minx || self.m_scissor[1] != miny || self.m_scissor[2] != width || self.m_scissor[3] != height)
-	{
-		self.m_scissorDirty = true;
-
-		self.m_scissor[0] = minx;
-		self.m_scissor[1] = miny;
-		self.m_scissor[2] = width;
-		self.m_scissor[3] = height;
-	}
+	self.m_graphicsState.setScissor(minx, miny, width, height);
 }
 
 void CommandBuffer::setFillMode(FillMode mode)
 {
 	ANKI_VK_SELF(CommandBufferImpl);
 	self.commandCommon();
-	self.m_state.setFillMode(mode);
+	self.m_graphicsState.setFillMode(mode);
 }
 
 void CommandBuffer::setCullMode(FaceSelectionBit mode)
 {
 	ANKI_VK_SELF(CommandBufferImpl);
 	self.commandCommon();
-	self.m_state.setCullMode(mode);
+	self.m_graphicsState.setCullMode(mode);
 }
 
 void CommandBuffer::setPolygonOffset(F32 factor, F32 units)
 {
 	ANKI_VK_SELF(CommandBufferImpl);
 	self.commandCommon();
-	self.m_state.setPolygonOffset(factor, units);
+	self.m_graphicsState.setPolygonOffset(factor, units);
 	vkCmdSetDepthBias(self.m_handle, factor, 0.0f, units);
 }
 
@@ -138,131 +122,77 @@ void CommandBuffer::setStencilOperations(FaceSelectionBit face, StencilOperation
 {
 	ANKI_VK_SELF(CommandBufferImpl);
 	self.commandCommon();
-	self.m_state.setStencilOperations(face, stencilFail, stencilPassDepthFail, stencilPassDepthPass);
+	self.m_graphicsState.setStencilOperations(face, stencilFail, stencilPassDepthFail, stencilPassDepthPass);
 }
 
 void CommandBuffer::setStencilCompareOperation(FaceSelectionBit face, CompareOperation comp)
 {
 	ANKI_VK_SELF(CommandBufferImpl);
 	self.commandCommon();
-	self.m_state.setStencilCompareOperation(face, comp);
+	self.m_graphicsState.setStencilCompareOperation(face, comp);
 }
 
 void CommandBuffer::setStencilCompareMask(FaceSelectionBit face, U32 mask)
 {
 	ANKI_VK_SELF(CommandBufferImpl);
 	self.commandCommon();
-
-	VkStencilFaceFlags flags = 0;
-
-	if(!!(face & FaceSelectionBit::kFront) && self.m_stencilCompareMasks[0] != mask)
-	{
-		self.m_stencilCompareMasks[0] = mask;
-		flags = VK_STENCIL_FACE_FRONT_BIT;
-	}
-
-	if(!!(face & FaceSelectionBit::kBack) && self.m_stencilCompareMasks[1] != mask)
-	{
-		self.m_stencilCompareMasks[1] = mask;
-		flags |= VK_STENCIL_FACE_BACK_BIT;
-	}
-
-	if(flags)
-	{
-		vkCmdSetStencilCompareMask(self.m_handle, flags, mask);
-	}
+	self.m_graphicsState.setStencilCompareMask(face, mask);
 }
 
 void CommandBuffer::setStencilWriteMask(FaceSelectionBit face, U32 mask)
 {
 	ANKI_VK_SELF(CommandBufferImpl);
 	self.commandCommon();
-
-	VkStencilFaceFlags flags = 0;
-
-	if(!!(face & FaceSelectionBit::kFront) && self.m_stencilWriteMasks[0] != mask)
-	{
-		self.m_stencilWriteMasks[0] = mask;
-		flags = VK_STENCIL_FACE_FRONT_BIT;
-	}
-
-	if(!!(face & FaceSelectionBit::kBack) && self.m_stencilWriteMasks[1] != mask)
-	{
-		self.m_stencilWriteMasks[1] = mask;
-		flags |= VK_STENCIL_FACE_BACK_BIT;
-	}
-
-	if(flags)
-	{
-		vkCmdSetStencilWriteMask(self.m_handle, flags, mask);
-	}
+	self.m_graphicsState.setStencilWriteMask(face, mask);
 }
 
 void CommandBuffer::setStencilReference(FaceSelectionBit face, U32 ref)
 {
 	ANKI_VK_SELF(CommandBufferImpl);
 	self.commandCommon();
-
-	VkStencilFaceFlags flags = 0;
-
-	if(!!(face & FaceSelectionBit::kFront) && self.m_stencilReferenceMasks[0] != ref)
-	{
-		self.m_stencilReferenceMasks[0] = ref;
-		flags = VK_STENCIL_FACE_FRONT_BIT;
-	}
-
-	if(!!(face & FaceSelectionBit::kBack) && self.m_stencilReferenceMasks[1] != ref)
-	{
-		self.m_stencilWriteMasks[1] = ref;
-		flags |= VK_STENCIL_FACE_BACK_BIT;
-	}
-
-	if(flags)
-	{
-		vkCmdSetStencilReference(self.m_handle, flags, ref);
-	}
+	self.m_graphicsState.setStencilReference(face, ref);
 }
 
 void CommandBuffer::setDepthWrite(Bool enable)
 {
 	ANKI_VK_SELF(CommandBufferImpl);
 	self.commandCommon();
-	self.m_state.setDepthWrite(enable);
+	self.m_graphicsState.setDepthWrite(enable);
 }
 
 void CommandBuffer::setDepthCompareOperation(CompareOperation op)
 {
 	ANKI_VK_SELF(CommandBufferImpl);
 	self.commandCommon();
-	self.m_state.setDepthCompareOperation(op);
+	self.m_graphicsState.setDepthCompareOperation(op);
 }
 
 void CommandBuffer::setAlphaToCoverage(Bool enable)
 {
 	ANKI_VK_SELF(CommandBufferImpl);
 	self.commandCommon();
-	self.m_state.setAlphaToCoverage(enable);
+	self.m_graphicsState.setAlphaToCoverage(enable);
 }
 
 void CommandBuffer::setColorChannelWriteMask(U32 attachment, ColorBit mask)
 {
 	ANKI_VK_SELF(CommandBufferImpl);
 	self.commandCommon();
-	self.m_state.setColorChannelWriteMask(attachment, mask);
+	self.m_graphicsState.setColorChannelWriteMask(attachment, mask);
 }
 
 void CommandBuffer::setBlendFactors(U32 attachment, BlendFactor srcRgb, BlendFactor dstRgb, BlendFactor srcA, BlendFactor dstA)
 {
 	ANKI_VK_SELF(CommandBufferImpl);
 	self.commandCommon();
-	self.m_state.setBlendFactors(attachment, srcRgb, dstRgb, srcA, dstA);
+	self.m_graphicsState.setBlendFactors(attachment, srcRgb, dstRgb, srcA, dstA);
 }
 
 void CommandBuffer::setBlendOperation(U32 attachment, BlendOperation funcRgb, BlendOperation funcA)
 {
 	ANKI_VK_SELF(CommandBufferImpl);
 	self.commandCommon();
-	self.m_state.setBlendOperation(attachment, funcRgb, funcA);
+	self.m_graphicsState.setBlendOperation(attachment, funcRgb, funcA);
 }
 
 void CommandBuffer::bindTexture(Register reg, const TextureView& texView)
@@ -380,7 +310,7 @@ void CommandBuffer::bindShaderProgram(ShaderProgram* prog)
 		self.m_graphicsProg = &impl;
 		self.m_computeProg = nullptr; // Unbind the compute prog. Doesn't work like vulkan
 		self.m_rtProg = nullptr; // See above
-		self.m_state.bindShaderProgram(&impl);
+		self.m_graphicsState.bindShaderProgram(&impl);
 
 		bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
 	}
@@ -557,21 +487,12 @@ void CommandBuffer::beginRenderPass(ConstWeakArray<RenderTarget> colorRts, Rende
 	info.renderArea.extent.height = height;
 
 	// State bookkeeping
-	self.m_state.beginRenderPass({colorFormats.getBegin(), colorRts.getSize()}, dsFormat, flipViewport);
-
-	// Re-set the viewport and scissor because sometimes they are set clamped
-	self.m_viewportDirty = true;
-	self.m_scissorDirty = true;
-
-	self.m_renderpassDrawsToDefaultFb = drawsToSwapchain;
+	self.m_graphicsState.beginRenderPass({colorFormats.getBegin(), colorRts.getSize()}, dsFormat, UVec2(fbWidth, fbHeight), drawsToSwapchain);
 	if(drawsToSwapchain)
 	{
 		self.m_renderedToDefaultFb = true;
 	}
 
-	self.m_renderpassWidth = fbWidth;
-	self.m_renderpassHeight = fbHeight;
-
 	// Finaly
 	vkCmdBeginRenderingKHR(self.m_handle, &info);
 }
@@ -586,7 +507,6 @@ void CommandBuffer::endRenderPass()
 #endif
 
 	self.commandCommon();
-	self.m_state.endRenderPass();
 	vkCmdEndRenderingKHR(self.m_handle);
 }
 
@@ -597,17 +517,13 @@ void CommandBuffer::setVrsRate(VrsRate rate)
 	ANKI_ASSERT(rate < VrsRate::kCount);
 	self.commandCommon();
 
-	if(self.m_vrsRate != rate)
-	{
-		self.m_vrsRate = rate;
-		self.m_vrsRateDirty = true;
-	}
+	ANKI_ASSERT(!"TODO");
 }
 
 void CommandBuffer::drawIndexed(PrimitiveTopology topology, U32 count, U32 instanceCount, U32 firstIndex, U32 baseVertex, U32 baseInstance)
 {
 	ANKI_VK_SELF(CommandBufferImpl);
-	self.m_state.setPrimitiveTopology(topology);
+	self.m_graphicsState.setPrimitiveTopology(topology);
 	self.drawcallCommon();
 	vkCmdDrawIndexed(self.m_handle, count, instanceCount, firstIndex, baseVertex, baseInstance);
 }
@@ -615,7 +531,7 @@ void CommandBuffer::drawIndexed(PrimitiveTopology topology, U32 count, U32 insta
 void CommandBuffer::draw(PrimitiveTopology topology, U32 count, U32 instanceCount, U32 first, U32 baseInstance)
 {
 	ANKI_VK_SELF(CommandBufferImpl);
-	self.m_state.setPrimitiveTopology(topology);
+	self.m_graphicsState.setPrimitiveTopology(topology);
 	self.drawcallCommon();
 	vkCmdDraw(self.m_handle, count, instanceCount, first, baseInstance);
 }
@@ -626,7 +542,7 @@ void CommandBuffer::drawIndirect(PrimitiveTopology topology, const BufferView& b
 	ANKI_ASSERT(drawCount > 0);
 
 	ANKI_VK_SELF(CommandBufferImpl);
-	self.m_state.setPrimitiveTopology(topology);
+	self.m_graphicsState.setPrimitiveTopology(topology);
 	self.drawcallCommon();
 
 	const BufferImpl& impl = static_cast<const BufferImpl&>(buff.getBuffer());
@@ -644,7 +560,7 @@ void CommandBuffer::drawIndexedIndirect(PrimitiveTopology topology, const Buffer
 	ANKI_ASSERT(drawCount > 0);
 
 	ANKI_VK_SELF(CommandBufferImpl);
-	self.m_state.setPrimitiveTopology(topology);
+	self.m_graphicsState.setPrimitiveTopology(topology);
 	self.drawcallCommon();
 
 	const BufferImpl& impl = static_cast<const BufferImpl&>(buff.getBuffer());
@@ -662,7 +578,7 @@ void CommandBuffer::drawIndexedIndirectCount(PrimitiveTopology topology, const B
 	ANKI_ASSERT(countBuffer.isValid());
 
 	ANKI_VK_SELF(CommandBufferImpl);
-	self.m_state.setPrimitiveTopology(topology);
+	self.m_graphicsState.setPrimitiveTopology(topology);
 	self.drawcallCommon();
 
 	ANKI_ASSERT(argBufferStride >= sizeof(DrawIndexedIndirectArgs));
@@ -691,7 +607,7 @@ void CommandBuffer::drawIndirectCount(PrimitiveTopology topology, const BufferVi
 	ANKI_ASSERT(countBuffer.isValid());
 
 	ANKI_VK_SELF(CommandBufferImpl);
-	self.m_state.setPrimitiveTopology(topology);
+	self.m_graphicsState.setPrimitiveTopology(topology);
 	self.drawcallCommon();
 
 	ANKI_ASSERT(argBufferStride >= sizeof(DrawIndirectArgs));
@@ -733,7 +649,7 @@ void CommandBuffer::drawMeshTasksIndirect(const BufferView& argBuffer, U32 drawC
 
 	ANKI_VK_SELF(CommandBufferImpl);
 
-	self.m_state.setPrimitiveTopology(PrimitiveTopology::kTriangles); // Not sure if that's needed
+	self.m_graphicsState.setPrimitiveTopology(PrimitiveTopology::kTriangles); // Not sure if that's needed
 	self.drawcallCommon();
 	vkCmdDrawMeshTasksIndirectEXT(self.m_handle, impl.getHandle(), argBuffer.getOffset(), drawCount, sizeof(DispatchIndirectArgs));
 }
@@ -1172,33 +1088,18 @@ void CommandBuffer::setPushConstants(const void* data, U32 dataSize)
 	ANKI_VK_SELF(CommandBufferImpl);
 	ANKI_ASSERT(data && dataSize && dataSize % 16 == 0);
 	const ShaderProgramImpl& prog = self.getBoundProgram();
-	ANKI_ASSERT(prog.getReflectionInfo().m_descriptor.m_pushConstantsSize == dataSize
+	ANKI_ASSERT(prog.getReflection().m_descriptor.m_pushConstantsSize == dataSize
 				&& "The bound program should have push constants equal to the \"dataSize\" parameter");
 
 	self.commandCommon();
 	self.m_descriptorState.setPushConstants(data, dataSize);
 }
 
-void CommandBuffer::setRasterizationOrder(RasterizationOrder order)
-{
-	ANKI_VK_SELF(CommandBufferImpl);
-	self.commandCommon();
-
-	if(!!(getGrManagerImpl().getExtensions() & VulkanExtensions::kAMD_rasterization_order))
-	{
-		self.m_state.setRasterizationOrder(order);
-	}
-}
-
 void CommandBuffer::setLineWidth(F32 width)
 {
 	ANKI_VK_SELF(CommandBufferImpl);
 	self.commandCommon();
-	vkCmdSetLineWidth(self.m_handle, width);
-
-#if ANKI_ASSERTIONS_ENABLED
-	self.m_lineWidthSet = true;
-#endif
+	self.m_graphicsState.setLineWidth(width);
 }
 
 void CommandBuffer::pushDebugMarker(CString name, Vec3 color)
@@ -1272,8 +1173,6 @@ Error CommandBufferImpl::init(const CommandBufferInitInfo& init)
 
 	m_descriptorState.init(m_pool);
 
-	m_state.setVrsCapable(getGrManagerImpl().getDeviceCapabilities().m_vrs);
-
 	m_debugMarkers = !!(getGrManagerImpl().getExtensions() & VulkanExtensions::kEXT_debug_utils);
 
 	return Error::kNone;
@@ -1317,7 +1216,7 @@ void CommandBufferImpl::beginRecording()
 	// Stats
 	if(!!(getGrManagerImpl().getExtensions() & VulkanExtensions::kKHR_pipeline_executable_properties))
 	{
-		m_state.setEnablePipelineStatistics(true);
+		m_graphicsState.setEnablePipelineStatistics(true);
 	}
 }
 
@@ -1374,70 +1273,11 @@ void CommandBufferImpl::drawcallCommon()
 	ANKI_ASSERT(m_insideRenderpass);
 
 	// Get or create ppline
-	Pipeline ppline;
-	Bool stateDirty;
-	m_graphicsProg->getPipelineFactory().getOrCreatePipeline(m_state, ppline, stateDirty);
-
-	if(stateDirty)
-	{
-		vkCmdBindPipeline(m_handle, VK_PIPELINE_BIND_POINT_GRAPHICS, ppline.getHandle());
-	}
+	m_graphicsProg->getGraphicsPipelineFactory().flushState(m_graphicsState, m_handle);
 
 	// Bind dsets
 	m_descriptorState.flush(m_handle, m_microCmdb->getDSAllocator());
 
-	// Flush viewport
-	if(m_viewportDirty) [[unlikely]]
-	{
-		const Bool flipvp = m_renderpassDrawsToDefaultFb;
-		VkViewport vp = computeViewport(&m_viewport[0], m_renderpassWidth, m_renderpassHeight, flipvp);
-
-		// Additional optimization
-		if(memcmp(&vp, &m_lastViewport, sizeof(vp)) != 0)
-		{
-			vkCmdSetViewport(m_handle, 0, 1, &vp);
-			m_lastViewport = vp;
-		}
-
-		m_viewportDirty = false;
-	}
-
-	// Flush scissor
-	if(m_scissorDirty) [[unlikely]]
-	{
-		const Bool flipvp = m_renderpassDrawsToDefaultFb;
-		VkRect2D scissor = computeScissor(&m_scissor[0], m_renderpassWidth, m_renderpassHeight, flipvp);
-
-		// Additional optimization
-		if(memcmp(&scissor, &m_lastScissor, sizeof(scissor)) != 0)
-		{
-			vkCmdSetScissor(m_handle, 0, 1, &scissor);
-			m_lastScissor = scissor;
-		}
-
-		m_scissorDirty = false;
-	}
-
-	// VRS
-	if(getGrManagerImpl().getDeviceCapabilities().m_vrs && m_vrsRateDirty)
-	{
-		const VkExtent2D extend = convertVrsShadingRate(m_vrsRate);
-		Array<VkFragmentShadingRateCombinerOpKHR, 2> combiner;
-		combiner[0] = VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR; // Keep pipeline rating over primitive
-		combiner[1] = VK_FRAGMENT_SHADING_RATE_COMBINER_OP_MAX_KHR; // Max of attachment and pipeline rates
-		vkCmdSetFragmentShadingRateKHR(m_handle, &extend, &combiner[0]);
-
-		m_vrsRateDirty = false;
-	}
-
-	// Some checks
-#if ANKI_ASSERTIONS_ENABLED
-	if(m_state.getPrimitiveTopology() == PrimitiveTopology::kLines || m_state.getPrimitiveTopology() == PrimitiveTopology::kLineStip)
-	{
-		ANKI_ASSERT(m_lineWidthSet == true);
-	}
-#endif
-
 	ANKI_TRACE_INC_COUNTER(VkDrawcall, 1);
 }
 

+ 2 - 61
AnKi/Gr/Vulkan/VkCommandBuffer.h

@@ -14,7 +14,7 @@
 #include <AnKi/Gr/Vulkan/VkPipelineQuery.h>
 #include <AnKi/Gr/Vulkan/VkBuffer.h>
 #include <AnKi/Gr/Vulkan/VkTexture.h>
-#include <AnKi/Gr/Vulkan/VkPipelineFactory.h>
+#include <AnKi/Gr/Vulkan/VkGraphicsState.h>
 #include <AnKi/Gr/Vulkan/VkGrManager.h>
 #include <AnKi/Util/List.h>
 
@@ -104,9 +104,6 @@ private:
 	Bool m_empty : 1 = true;
 	Bool m_beganRecording : 1 = false;
 	Bool m_debugMarkers : 1 = false;
-	Bool m_renderpassDrawsToDefaultFb : 1 = false;
-	U32 m_renderpassWidth = 0;
-	U32 m_renderpassHeight = 0;
 #if ANKI_ASSERTIONS_ENABLED
 	U32 m_commandCount = 0;
 	U32 m_debugMarkersPushed = 0;
@@ -114,31 +111,13 @@ private:
 	Bool m_insideRenderpass = false;
 #endif
 
-	PipelineStateTracker m_state;
+	GraphicsStateTracker m_graphicsState;
 	DescriptorState m_descriptorState;
 
 	ShaderProgramImpl* m_graphicsProg ANKI_DEBUG_CODE(= nullptr); ///< Last bound graphics program
 	ShaderProgramImpl* m_computeProg ANKI_DEBUG_CODE(= nullptr);
 	ShaderProgramImpl* m_rtProg ANKI_DEBUG_CODE(= nullptr);
 
-	/// @name state_opts
-	/// @{
-	Array<U32, 4> m_viewport = {0, 0, 0, 0};
-	Array<U32, 4> m_scissor = {0, 0, kMaxU32, kMaxU32};
-	VkViewport m_lastViewport = {};
-	Bool m_viewportDirty = true;
-	Bool m_scissorDirty = true;
-	VkRect2D m_lastScissor = {{-1, -1}, {kMaxU32, kMaxU32}};
-	Array<U32, 2> m_stencilCompareMasks = {0x5A5A5A5A, 0x5A5A5A5A}; ///< Use a stupid number to initialize.
-	Array<U32, 2> m_stencilWriteMasks = {0x5A5A5A5A, 0x5A5A5A5A};
-	Array<U32, 2> m_stencilReferenceMasks = {0x5A5A5A5A, 0x5A5A5A5A};
-#if ANKI_ASSERTIONS_ENABLED
-	Bool m_lineWidthSet = false;
-#endif
-	Bool m_vrsRateDirty = true;
-	VrsRate m_vrsRate = VrsRate::k1x1;
-	/// @}
-
 	/// Some common operations per command.
 	ANKI_FORCE_INLINE void commandCommon()
 	{
@@ -167,44 +146,6 @@ private:
 
 	void beginRecording();
 
-	static VkViewport computeViewport(U32* viewport, U32 fbWidth, U32 fbHeight, Bool flipvp)
-	{
-		const U32 minx = viewport[0];
-		const U32 miny = viewport[1];
-		const U32 width = min<U32>(fbWidth, viewport[2]);
-		const U32 height = min<U32>(fbHeight, viewport[3]);
-		ANKI_ASSERT(width > 0 && height > 0);
-		ANKI_ASSERT(minx + width <= fbWidth);
-		ANKI_ASSERT(miny + height <= fbHeight);
-
-		VkViewport s = {};
-		s.x = F32(minx);
-		s.y = (flipvp) ? F32(fbHeight - miny) : F32(miny); // Move to the bottom;
-		s.width = F32(width);
-		s.height = (flipvp) ? -F32(height) : F32(height);
-		s.minDepth = 0.0f;
-		s.maxDepth = 1.0f;
-		return s;
-	}
-
-	static VkRect2D computeScissor(U32* scissor, U32 fbWidth, U32 fbHeight, Bool flipvp)
-	{
-		const U32 minx = scissor[0];
-		const U32 miny = scissor[1];
-		const U32 width = min<U32>(fbWidth, scissor[2]);
-		const U32 height = min<U32>(fbHeight, scissor[3]);
-		ANKI_ASSERT(minx + width <= fbWidth);
-		ANKI_ASSERT(miny + height <= fbHeight);
-
-		VkRect2D out = {};
-		out.extent.width = width;
-		out.extent.height = height;
-		out.offset.x = minx;
-		out.offset.y = (flipvp) ? (fbHeight - (miny + height)) : miny;
-
-		return out;
-	}
-
 	const ShaderProgramImpl& getBoundProgram()
 	{
 		if(m_graphicsProg)

+ 0 - 19
AnKi/Gr/Vulkan/VkCommon.h

@@ -289,25 +289,6 @@ static_assert(!(BufferUsageBit::kAll & PrivateBufferUsageBit::kAllPrivate), "Upd
 	return out;
 }
 
-[[nodiscard]] inline VkRasterizationOrderAMD convertRasterizationOrder(RasterizationOrder ak)
-{
-	VkRasterizationOrderAMD out;
-	switch(ak)
-	{
-	case RasterizationOrder::kOrdered:
-		out = VK_RASTERIZATION_ORDER_STRICT_AMD;
-		break;
-	case RasterizationOrder::kRelaxed:
-		out = VK_RASTERIZATION_ORDER_RELAXED_AMD;
-		break;
-	default:
-		ANKI_ASSERT(0);
-		out = VK_RASTERIZATION_ORDER_STRICT_AMD;
-	}
-
-	return out;
-}
-
 [[nodiscard]] inline VkAccelerationStructureTypeKHR convertAccelerationStructureType(AccelerationStructureType ak)
 {
 	VkAccelerationStructureTypeKHR out;

+ 537 - 0
AnKi/Gr/Vulkan/VkGraphicsState.cpp

@@ -0,0 +1,537 @@
+// Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <AnKi/Gr/Vulkan/VkGraphicsState.h>
+#include <AnKi/Gr/BackendCommon/Functions.h>
+#include <AnKi/Gr/Vulkan/VkGrManager.h>
+#include <AnKi/Gr/Vulkan/VkShaderProgram.h>
+#include <AnKi/Util/Filesystem.h>
+
+namespace anki {
+
+static NumericCVar<PtrSize> g_diskShaderCacheMaxSizeCVar(CVarSubsystem::kGr, "DiskShaderCacheMaxSize", 128_MB, 1_MB, 1_GB,
+														 "Max size of the pipeline cache file");
+
+static VkViewport computeViewport(U32* viewport, U32 fbWidth, U32 fbHeight, Bool flipvp)
+{
+	const U32 minx = viewport[0];
+	const U32 miny = viewport[1];
+	const U32 width = min<U32>(fbWidth, viewport[2]);
+	const U32 height = min<U32>(fbHeight, viewport[3]);
+	ANKI_ASSERT(width > 0 && height > 0);
+	ANKI_ASSERT(minx + width <= fbWidth);
+	ANKI_ASSERT(miny + height <= fbHeight);
+
+	const VkViewport s = {.x = F32(minx),
+						  .y = (flipvp) ? F32(fbHeight - miny) : F32(miny), // Move to the bottom
+						  .width = F32(width),
+						  .height = (flipvp) ? -F32(height) : F32(height),
+						  .minDepth = 0.0f,
+						  .maxDepth = 1.0f};
+
+	return s;
+}
+
+static VkRect2D computeScissor(U32* scissor, U32 fbWidth, U32 fbHeight, Bool flipvp)
+{
+	const U32 minx = scissor[0];
+	const U32 miny = scissor[1];
+	const U32 width = min<U32>(fbWidth, scissor[2]);
+	const U32 height = min<U32>(fbHeight, scissor[3]);
+	ANKI_ASSERT(width > 0 && height > 0);
+	ANKI_ASSERT(minx + width <= fbWidth);
+	ANKI_ASSERT(miny + height <= fbHeight);
+
+	VkRect2D out = {};
+	out.extent.width = width;
+	out.extent.height = height;
+	out.offset.x = minx;
+	out.offset.y = (flipvp) ? (fbHeight - (miny + height)) : miny;
+
+	return out;
+}
+
+GraphicsPipelineFactory::~GraphicsPipelineFactory()
+{
+	for(auto pso : m_map)
+	{
+		vkDestroyPipeline(getVkDevice(), pso, nullptr);
+	}
+}
+
+void GraphicsPipelineFactory::flushState(GraphicsStateTracker& state, VkCommandBuffer& cmdb)
+{
+	const GraphicsStateTracker::StaticState& staticState = state.m_staticState;
+	GraphicsStateTracker::DynamicState& dynState = state.m_dynState;
+
+	// Set dynamic state
+	const auto& ss = staticState.m_stencil;
+	const Bool stencilTestEnabled = anki::stencilTestEnabled(ss.m_face[0].m_fail, ss.m_face[0].m_stencilPassDepthFail,
+															 ss.m_face[0].m_stencilPassDepthPass, ss.m_face[0].m_compare)
+									|| anki::stencilTestEnabled(ss.m_face[1].m_fail, ss.m_face[1].m_stencilPassDepthFail,
+																ss.m_face[1].m_stencilPassDepthPass, ss.m_face[1].m_compare);
+
+	const Bool hasStencilRt =
+		staticState.m_misc.m_depthStencilFormat != Format::kNone && getFormatInfo(staticState.m_misc.m_depthStencilFormat).isStencil();
+
+	if(stencilTestEnabled && hasStencilRt && dynState.m_stencilCompareMaskDirty)
+	{
+		ANKI_ASSERT(dynState.m_stencilFaces[0].m_compareMask != 0x5A5A5A5A && dynState.m_stencilFaces[1].m_compareMask != 0x5A5A5A5A);
+		dynState.m_stencilCompareMaskDirty = false;
+
+		if(dynState.m_stencilFaces[0].m_compareMask == dynState.m_stencilFaces[1].m_compareMask)
+		{
+			vkCmdSetStencilCompareMask(cmdb, VK_STENCIL_FACE_FRONT_AND_BACK, dynState.m_stencilFaces[0].m_compareMask);
+		}
+		else
+		{
+			vkCmdSetStencilCompareMask(cmdb, VK_STENCIL_FACE_FRONT_BIT, dynState.m_stencilFaces[0].m_compareMask);
+			vkCmdSetStencilCompareMask(cmdb, VK_STENCIL_FACE_BACK_BIT, dynState.m_stencilFaces[1].m_compareMask);
+		}
+	}
+
+	if(stencilTestEnabled && hasStencilRt && dynState.m_stencilWriteMaskDirty)
+	{
+		ANKI_ASSERT(dynState.m_stencilFaces[0].m_writeMask != 0x5A5A5A5A && dynState.m_stencilFaces[1].m_writeMask != 0x5A5A5A5A);
+		dynState.m_stencilWriteMaskDirty = false;
+
+		if(dynState.m_stencilFaces[0].m_writeMask == dynState.m_stencilFaces[1].m_writeMask)
+		{
+			vkCmdSetStencilWriteMask(cmdb, VK_STENCIL_FACE_FRONT_AND_BACK, dynState.m_stencilFaces[0].m_writeMask);
+		}
+		else
+		{
+			vkCmdSetStencilWriteMask(cmdb, VK_STENCIL_FACE_FRONT_BIT, dynState.m_stencilFaces[0].m_writeMask);
+			vkCmdSetStencilWriteMask(cmdb, VK_STENCIL_FACE_BACK_BIT, dynState.m_stencilFaces[1].m_writeMask);
+		}
+	}
+
+	if(stencilTestEnabled && hasStencilRt && dynState.m_stencilRefDirty)
+	{
+		ANKI_ASSERT(dynState.m_stencilFaces[0].m_ref != 0x5A5A5A5A && dynState.m_stencilFaces[1].m_ref != 0x5A5A5A5A);
+		dynState.m_stencilRefDirty = false;
+
+		if(dynState.m_stencilFaces[0].m_ref == dynState.m_stencilFaces[1].m_ref)
+		{
+			vkCmdSetStencilReference(cmdb, VK_STENCIL_FACE_FRONT_AND_BACK, dynState.m_stencilFaces[0].m_ref);
+		}
+		else
+		{
+			vkCmdSetStencilReference(cmdb, VK_STENCIL_FACE_FRONT_BIT, dynState.m_stencilFaces[0].m_ref);
+			vkCmdSetStencilReference(cmdb, VK_STENCIL_FACE_BACK_BIT, dynState.m_stencilFaces[1].m_ref);
+		}
+	}
+
+	const Bool hasDepthRt =
+		staticState.m_misc.m_depthStencilFormat != Format::kNone && getFormatInfo(staticState.m_misc.m_depthStencilFormat).isDepth();
+
+	const Bool depthTestEnabled = anki::depthTestEnabled(staticState.m_depth.m_compare, staticState.m_depth.m_writeEnabled);
+
+	if(hasDepthRt && depthTestEnabled && dynState.m_depthBiasDirty)
+	{
+		dynState.m_depthBiasDirty = false;
+
+		vkCmdSetDepthBias(cmdb, dynState.m_depthBiasConstantFactor, dynState.m_depthBiasClamp, dynState.m_depthBiasSlopeFactor);
+	}
+
+	if(dynState.m_viewportDirty)
+	{
+		ANKI_ASSERT(dynState.m_viewport[2] != 0 && dynState.m_viewport[3] != 0);
+		dynState.m_viewportDirty = false;
+
+		const Bool flipVp = staticState.m_misc.m_rendersToSwapchain;
+		const VkViewport vp = computeViewport(dynState.m_viewport.getBegin(), state.m_rtsSize.x(), state.m_rtsSize.y(), flipVp);
+
+		vkCmdSetViewport(cmdb, 0, 1, &vp);
+	}
+
+	if(dynState.m_scissorDirty)
+	{
+		dynState.m_scissorDirty = false;
+
+		const Bool flipVp = staticState.m_misc.m_rendersToSwapchain;
+		const VkRect2D rect = computeScissor(dynState.m_scissor.getBegin(), state.m_rtsSize.x(), state.m_rtsSize.y(), flipVp);
+
+		vkCmdSetScissor(cmdb, 0, 1, &rect);
+	}
+
+	if(dynState.m_lineWidthDirty)
+	{
+		dynState.m_lineWidthDirty = false;
+		vkCmdSetLineWidth(cmdb, dynState.m_lineWidth);
+	}
+
+	// Static state
+	const Bool rebindPso = state.updateHashes();
+
+	// Find the PSO
+	VkPipeline pso = VK_NULL_HANDLE;
+	{
+		RLockGuard lock(m_mtx);
+
+		auto it = m_map.find(state.m_globalHash);
+		if(it != m_map.getEnd())
+		{
+			pso = *it;
+		}
+	}
+
+	if(pso) [[likely]]
+	{
+		if(rebindPso)
+		{
+			vkCmdBindPipeline(cmdb, VK_PIPELINE_BIND_POINT_GRAPHICS, pso);
+		}
+
+		return;
+	}
+
+	// PSO not found, proactively create it WITHOUT a lock (we dont't want to serialize pipeline creation)
+
+	const ShaderProgramImpl& prog = static_cast<const ShaderProgramImpl&>(*staticState.m_shaderProg);
+
+	VkGraphicsPipelineCreateInfo ci = {};
+	ci.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
+
+	if(staticState.m_misc.m_pipelineStatisticsEnabled)
+	{
+		ci.flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR;
+	}
+
+	ci.pStages = prog.getShaderCreateInfos(ci.stageCount);
+
+	// Vertex stuff
+	Array<VkVertexInputBindingDescription, U32(VertexAttributeSemantic::kCount)> vertBindings;
+	Array<VkVertexInputAttributeDescription, U32(VertexAttributeSemantic::kCount)> attribs;
+
+	VkPipelineVertexInputStateCreateInfo vertCi = {};
+	vertCi.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
+	vertCi.pVertexAttributeDescriptions = &attribs[0];
+	vertCi.pVertexBindingDescriptions = &vertBindings[0];
+
+	BitSet<U32(VertexAttributeSemantic::kCount), U8> bindingSet = {false};
+	for(VertexAttributeSemantic semantic : EnumIterable<VertexAttributeSemantic>())
+	{
+		if(staticState.m_vert.m_activeAttribs.get(semantic))
+		{
+			VkVertexInputAttributeDescription& attrib = attribs[vertCi.vertexAttributeDescriptionCount++];
+			attrib.binding = staticState.m_vert.m_attribs[semantic].m_binding;
+			attrib.format = convertFormat(staticState.m_vert.m_attribs[semantic].m_fmt);
+			attrib.location = staticState.m_vert.m_attribs[semantic].m_semanticToVertexAttributeLocation;
+			attrib.offset = staticState.m_vert.m_attribs[semantic].m_relativeOffset;
+
+			if(!bindingSet.get(attrib.binding))
+			{
+				bindingSet.set(attrib.binding);
+
+				VkVertexInputBindingDescription& binding = vertBindings[vertCi.vertexBindingDescriptionCount++];
+
+				binding.binding = attrib.binding;
+				binding.inputRate = convertVertexStepRate(staticState.m_vert.m_bindings[attrib.binding].m_stepRate);
+				binding.stride = staticState.m_vert.m_bindings[attrib.binding].m_stride;
+			}
+		}
+	}
+
+	ci.pVertexInputState = &vertCi;
+
+	// IA
+	VkPipelineInputAssemblyStateCreateInfo iaCi = {};
+	iaCi.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
+	iaCi.primitiveRestartEnable = staticState.m_ia.m_primitiveRestartEnabled;
+	iaCi.topology = convertTopology(staticState.m_ia.m_topology);
+	ci.pInputAssemblyState = &iaCi;
+
+	// Viewport
+	VkPipelineViewportStateCreateInfo vpCi = {};
+	vpCi.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
+	vpCi.scissorCount = 1;
+	vpCi.viewportCount = 1;
+	ci.pViewportState = &vpCi;
+
+	// Raster
+	VkPipelineRasterizationStateCreateInfo rastCi = {};
+	rastCi.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
+	rastCi.depthClampEnable = false;
+	rastCi.rasterizerDiscardEnable = false;
+	rastCi.polygonMode = convertFillMode(staticState.m_rast.m_fillMode);
+	rastCi.cullMode = convertCullMode(staticState.m_rast.m_cullMode);
+	rastCi.frontFace = (!staticState.m_misc.m_rendersToSwapchain) ? VK_FRONT_FACE_CLOCKWISE : VK_FRONT_FACE_COUNTER_CLOCKWISE; // For viewport flip
+	rastCi.depthBiasEnable = staticState.m_rast.m_depthBiasEnabled;
+	rastCi.lineWidth = 1.0f;
+	ci.pRasterizationState = &rastCi;
+
+	// MS
+	VkPipelineMultisampleStateCreateInfo msCi = {};
+	msCi.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
+	msCi.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
+	ci.pMultisampleState = &msCi;
+
+	// Depth stencil
+	VkPipelineDepthStencilStateCreateInfo dsCi;
+	if(hasDepthRt || hasStencilRt)
+	{
+		dsCi = {};
+		dsCi.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
+
+		if(hasDepthRt)
+		{
+			dsCi.depthTestEnable = depthTestEnabled;
+			dsCi.depthWriteEnable = staticState.m_depth.m_writeEnabled;
+			dsCi.depthCompareOp = convertCompareOp(staticState.m_depth.m_compare);
+		}
+
+		if(hasStencilRt)
+		{
+			const auto& ss = staticState.m_stencil;
+
+			dsCi.stencilTestEnable = stencilTestEnabled;
+			dsCi.front.failOp = convertStencilOp(ss.m_face[0].m_fail);
+			dsCi.front.passOp = convertStencilOp(ss.m_face[0].m_stencilPassDepthPass);
+			dsCi.front.depthFailOp = convertStencilOp(ss.m_face[0].m_stencilPassDepthFail);
+			dsCi.front.compareOp = convertCompareOp(ss.m_face[0].m_compare);
+			dsCi.back.failOp = convertStencilOp(ss.m_face[1].m_fail);
+			dsCi.back.passOp = convertStencilOp(ss.m_face[1].m_stencilPassDepthPass);
+			dsCi.back.depthFailOp = convertStencilOp(ss.m_face[1].m_stencilPassDepthFail);
+			dsCi.back.compareOp = convertCompareOp(ss.m_face[1].m_compare);
+		}
+
+		ci.pDepthStencilState = &dsCi;
+	}
+
+	// Color/blend
+	Array<VkPipelineColorBlendAttachmentState, kMaxColorRenderTargets> colAttachments = {};
+
+	VkPipelineColorBlendStateCreateInfo colCi = {};
+	colCi.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
+	if(staticState.m_misc.m_colorRtMask.getAnySet())
+	{
+		colCi.attachmentCount = staticState.m_misc.m_colorRtMask.getSetBitCount();
+		colCi.pAttachments = &colAttachments[0];
+
+		for(U i = 0; i < colCi.attachmentCount; ++i)
+		{
+			VkPipelineColorBlendAttachmentState& out = colAttachments[i];
+			const auto& in = staticState.m_blend.m_colorRts[i];
+
+			out.blendEnable = blendingEnabled(in.m_srcRgb, in.m_dstRgb, in.m_srcA, in.m_dstA, in.m_funcRgb, in.m_funcA);
+			out.srcColorBlendFactor = convertBlendFactor(in.m_srcRgb);
+			out.dstColorBlendFactor = convertBlendFactor(in.m_dstRgb);
+			out.srcAlphaBlendFactor = convertBlendFactor(in.m_srcA);
+			out.dstAlphaBlendFactor = convertBlendFactor(in.m_dstA);
+			out.colorBlendOp = convertBlendOperation(in.m_funcRgb);
+			out.alphaBlendOp = convertBlendOperation(in.m_funcA);
+
+			out.colorWriteMask = convertColorWriteMask(in.m_channelWriteMask);
+		}
+
+		ci.pColorBlendState = &colCi;
+	}
+
+	// Renderpass related (Dynamic rendering)
+	Array<VkFormat, kMaxColorRenderTargets> dynamicRenderingAttachmentFormats = {};
+	VkPipelineRenderingCreateInfoKHR dynRendering = {};
+	dynRendering.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR;
+	dynRendering.colorAttachmentCount = staticState.m_misc.m_colorRtMask.getSetBitCount();
+	dynRendering.pColorAttachmentFormats = dynamicRenderingAttachmentFormats.getBegin();
+	for(U i = 0; i < kMaxColorRenderTargets; ++i)
+	{
+		dynamicRenderingAttachmentFormats[i] =
+			(staticState.m_misc.m_colorRtMask.get(i)) ? convertFormat(staticState.m_misc.m_colorRtFormats[i]) : VK_FORMAT_UNDEFINED;
+	}
+
+	if(hasDepthRt)
+	{
+		dynRendering.depthAttachmentFormat = convertFormat(staticState.m_misc.m_depthStencilFormat);
+	}
+
+	if(hasStencilRt)
+	{
+		dynRendering.stencilAttachmentFormat = convertFormat(staticState.m_misc.m_depthStencilFormat);
+	}
+
+	appendPNextList(ci, &dynRendering);
+
+	// Almost all state is dynamic. Depth bias is static
+	VkPipelineDynamicStateCreateInfo dynCi = {};
+	dynCi.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
+
+	static constexpr Array<VkDynamicState, 10> kDyn = {
+		{VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, VK_DYNAMIC_STATE_BLEND_CONSTANTS, VK_DYNAMIC_STATE_DEPTH_BOUNDS,
+		 VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE, VK_DYNAMIC_STATE_LINE_WIDTH,
+		 VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_FRAGMENT_SHADING_RATE_KHR}};
+
+	dynCi.dynamicStateCount = (getGrManagerImpl().getDeviceCapabilities().m_vrs) ? kDyn.getSize() : (kDyn.getSize() - 1);
+	dynCi.pDynamicStates = &kDyn[0];
+	ci.pDynamicState = &dynCi;
+
+	// The rest
+	ci.layout = prog.getPipelineLayout().getHandle();
+	ci.subpass = 0;
+
+	// Create the pipeline
+	{
+		ANKI_TRACE_SCOPED_EVENT(VkPipelineCreate);
+
+#if ANKI_PLATFORM_MOBILE
+		if(PipelineCache::getSingleton().m_globalCreatePipelineMtx)
+		{
+			PipelineCache::getSingleton().m_globalCreatePipelineMtx->lock();
+		}
+#endif
+
+		ANKI_VK_CHECKF(vkCreateGraphicsPipelines(getVkDevice(), PipelineCache::getSingleton().m_cacheHandle, 1, &ci, nullptr, &pso));
+
+#if ANKI_PLATFORM_MOBILE
+		if(PipelineCache::getSingleton().m_globalCreatePipelineMtx)
+		{
+			PipelineCache::getSingleton().m_globalCreatePipelineMtx->unlock();
+		}
+#endif
+	}
+
+	// Now try to add the PSO to the hashmap
+	{
+		WLockGuard lock(m_mtx);
+
+		auto it = m_map.find(state.m_globalHash);
+		if(it == m_map.getEnd())
+		{
+			// Not found, add it
+			m_map.emplace(state.m_globalHash, pso);
+		}
+		else
+		{
+			// Found, remove the PSO that was proactively created and use the old one
+			vkDestroyPipeline(getVkDevice(), pso, nullptr);
+			pso = *it;
+		}
+	}
+
+	// Final thing, bind the PSO
+	vkCmdBindPipeline(cmdb, VK_PIPELINE_BIND_POINT_GRAPHICS, pso);
+}
+
+Error PipelineCache::init(CString cacheDir)
+{
+	ANKI_ASSERT(cacheDir);
+	m_dumpSize = g_diskShaderCacheMaxSizeCVar.get();
+	m_dumpFilename.sprintf("%s/VkPipelineCache", cacheDir.cstr());
+
+	// Try read the pipeline cache file.
+	GrDynamicArray<U8, PtrSize> diskDump;
+	if(fileExists(m_dumpFilename.toCString()))
+	{
+		File file;
+		ANKI_CHECK(file.open(m_dumpFilename.toCString(), FileOpenFlag::kBinary | FileOpenFlag::kRead));
+
+		const PtrSize diskDumpSize = file.getSize();
+		if(diskDumpSize <= sizeof(U8) * VK_UUID_SIZE)
+		{
+			ANKI_VK_LOGI("Pipeline cache dump appears to be empty: %s", &m_dumpFilename[0]);
+		}
+		else
+		{
+			// Get current pipeline UUID and compare it with the cache's
+			VkPhysicalDeviceProperties props;
+			vkGetPhysicalDeviceProperties(getGrManagerImpl().getPhysicalDevice(), &props);
+
+			Array<U8, VK_UUID_SIZE> cacheUuid;
+			ANKI_CHECK(file.read(&cacheUuid[0], VK_UUID_SIZE));
+
+			if(memcmp(&cacheUuid[0], &props.pipelineCacheUUID[0], VK_UUID_SIZE) != 0)
+			{
+				ANKI_VK_LOGI("Pipeline cache dump is not compatible with the current device: %s", &m_dumpFilename[0]);
+			}
+			else
+			{
+				diskDump.resize(diskDumpSize - VK_UUID_SIZE);
+				ANKI_CHECK(file.read(&diskDump[0], diskDumpSize - VK_UUID_SIZE));
+			}
+		}
+	}
+	else
+	{
+		ANKI_VK_LOGI("Pipeline cache dump not found: %s", &m_dumpFilename[0]);
+	}
+
+	// Create the cache
+	VkPipelineCacheCreateInfo ci = {};
+	ci.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
+	if(diskDump.getSize())
+	{
+		ANKI_VK_LOGI("Will load %zu bytes of pipeline cache", diskDump.getSize());
+		ci.initialDataSize = diskDump.getSize();
+		ci.pInitialData = &diskDump[0];
+	}
+
+	ANKI_VK_CHECK(vkCreatePipelineCache(getVkDevice(), &ci, nullptr, &m_cacheHandle));
+
+#if ANKI_PLATFORM_MOBILE
+	ANKI_ASSERT(GrManager::getSingleton().getDeviceCapabilities() != GpuVendor::kNone);
+	if(GrManager::getSingleton().getDeviceCapabilities().m_gpuVendor == GpuVendor::kQualcomm)
+	{
+		// Calling vkCreateGraphicsPipeline from multiple threads crashes qualcomm's compiler
+		ANKI_VK_LOGI("Enabling workaround for vkCreateGraphicsPipeline crashing when called from multiple threads");
+		m_globalCreatePipelineMtx = anki::newInstance<Mutex>(GrMemoryPool::getSingleton());
+	}
+#endif
+
+	return Error::kNone;
+}
+
+void PipelineCache::destroy()
+{
+	const Error err = destroyInternal();
+	if(err)
+	{
+		ANKI_VK_LOGE("An error occurred while storing the pipeline cache to disk. Will ignore");
+	}
+
+	m_dumpFilename.destroy();
+}
+
+Error PipelineCache::destroyInternal()
+{
+#if ANKI_PLATFORM_MOBILE
+	deleteInstance(GrMemoryPool::getSingleton(), m_globalCreatePipelineMtx);
+#endif
+
+	if(m_cacheHandle)
+	{
+		// Get size of cache
+		size_t size = 0;
+		ANKI_VK_CHECK(vkGetPipelineCacheData(getVkDevice(), m_cacheHandle, &size, nullptr));
+		size = min(size, m_dumpSize);
+
+		if(size > 0)
+		{
+			// Read cache
+			GrDynamicArray<U8, PtrSize> cacheData;
+			cacheData.resize(size);
+			ANKI_VK_CHECK(vkGetPipelineCacheData(getVkDevice(), m_cacheHandle, &size, &cacheData[0]));
+
+			// Write file
+			File file;
+			ANKI_CHECK(file.open(&m_dumpFilename[0], FileOpenFlag::kBinary | FileOpenFlag::kWrite));
+
+			VkPhysicalDeviceProperties props;
+			vkGetPhysicalDeviceProperties(getGrManagerImpl().getPhysicalDevice(), &props);
+
+			ANKI_CHECK(file.write(&props.pipelineCacheUUID[0], VK_UUID_SIZE));
+			ANKI_CHECK(file.write(&cacheData[0], size));
+
+			ANKI_VK_LOGI("Dumped %zu bytes of the pipeline cache", size);
+		}
+
+		// Destroy cache
+		vkDestroyPipelineCache(getVkDevice(), m_cacheHandle, nullptr);
+		m_cacheHandle = VK_NULL_HANDLE;
+	}
+
+	return Error::kNone;
+}
+
+} // end namespace anki

+ 58 - 0
AnKi/Gr/Vulkan/VkGraphicsState.h

@@ -0,0 +1,58 @@
+// Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <AnKi/Gr/Vulkan/VkCommon.h>
+#include <AnKi/Gr/ShaderProgram.h>
+#include <AnKi/Gr/BackendCommon/GraphicsStateTracker.h>
+#include <AnKi/Util/HashMap.h>
+
+namespace anki {
+
+/// @addtogroup vulkan
+/// @{
+
+class GraphicsPipelineFactory
+{
+public:
+	~GraphicsPipelineFactory();
+
+	/// Write state to the command buffer.
+	/// @note It's thread-safe.
+	void flushState(GraphicsStateTracker& state, VkCommandBuffer& cmdb);
+
+private:
+	GrHashMap<U64, VkPipeline> m_map;
+	RWMutex m_mtx;
+};
+
+/// On disk pipeline cache.
+class PipelineCache : public MakeSingleton<PipelineCache>
+{
+public:
+	VkPipelineCache m_cacheHandle = VK_NULL_HANDLE;
+#if ANKI_PLATFORM_MOBILE
+	/// Workaround a bug in Qualcomm
+	Mutex* m_globalCreatePipelineMtx = nullptr;
+#endif
+
+	~PipelineCache()
+	{
+		destroy();
+	}
+
+	Error init(CString cacheDir);
+
+private:
+	GrString m_dumpFilename;
+	PtrSize m_dumpSize = 0;
+
+	void destroy();
+	Error destroyInternal();
+};
+/// @}
+
+} // end namespace anki

+ 0 - 634
AnKi/Gr/Vulkan/VkPipelineFactory.cpp

@@ -1,634 +0,0 @@
-// Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <AnKi/Gr/Vulkan/VkPipelineFactory.h>
-#include <AnKi/Gr/Vulkan/VkGrManager.h>
-#include <AnKi/Gr/BackendCommon/Functions.h>
-#include <AnKi/Util/Tracer.h>
-#include <AnKi/Util/Filesystem.h>
-
-namespace anki {
-
-static NumericCVar<PtrSize> g_diskShaderCacheMaxSizeCVar(CVarSubsystem::kGr, "DiskShaderCacheMaxSize", 128_MB, 1_MB, 1_GB,
-														 "Max size of the pipeline cache file");
-
-void PipelineStateTracker::reset()
-{
-	m_state.reset();
-	m_hashes = {};
-	m_dirty = {};
-	m_set = {};
-	m_shaderVertexAttributeMask.unsetAll();
-	m_shaderColorAttachmentWritemask.unsetAll();
-	m_fbDepth = false;
-	m_fbStencil = false;
-	m_rendersToSwapchain = false;
-	m_fbColorAttachmentCount = 0;
-}
-
-Bool PipelineStateTracker::updateHashes()
-{
-	Bool stateDirty = false;
-
-	// Prog
-	if(m_dirty.m_prog)
-	{
-		m_dirty.m_prog = false;
-		stateDirty = true;
-		m_hashes.m_prog = m_state.m_prog->getUuid();
-	}
-
-	// Vertex
-	if(m_dirty.m_attribs.getAnySet() || m_dirty.m_vertBindings.getAnySet())
-	{
-		for(VertexAttributeSemantic i : EnumIterable<VertexAttributeSemantic>())
-		{
-			if(m_shaderVertexAttributeMask.get(i))
-			{
-				ANKI_ASSERT(m_set.m_attribs.get(i) && "Forgot to set the attribute");
-
-				Bool dirty = false;
-				if(m_dirty.m_attribs.get(i))
-				{
-					m_dirty.m_attribs.unset(i);
-					dirty = true;
-				}
-
-				const U binding = m_state.m_vertex.m_attributes[i].m_binding;
-				ANKI_ASSERT(m_set.m_vertBindings.get(binding) && "Forgot to set a vertex binding");
-
-				if(m_dirty.m_vertBindings.get(binding))
-				{
-					m_dirty.m_vertBindings.unset(binding);
-					dirty = true;
-				}
-
-				if(dirty)
-				{
-					m_hashes.m_vertexAttribs[i] = computeHash(&m_state.m_vertex.m_attributes[i], sizeof(m_state.m_vertex.m_attributes[i]));
-					m_hashes.m_vertexAttribs[i] =
-						appendHash(&m_state.m_vertex.m_bindings[i], sizeof(m_state.m_vertex.m_bindings[i]), m_hashes.m_vertexAttribs[i]);
-
-					stateDirty = true;
-				}
-			}
-		}
-	}
-
-	// IA
-	if(m_dirty.m_inputAssembler)
-	{
-		m_dirty.m_inputAssembler = false;
-		stateDirty = true;
-		m_hashes.m_ia = computeHash(&m_state.m_inputAssembler, sizeof(m_state.m_inputAssembler));
-	}
-
-	// Rasterizer
-	if(m_dirty.m_rasterizer)
-	{
-		m_dirty.m_rasterizer = false;
-		stateDirty = true;
-		m_hashes.m_raster = computeHash(&m_state.m_rasterizer, sizeof(m_state.m_rasterizer));
-	}
-
-	// Depth
-	if(m_fbDepth && m_dirty.m_depth)
-	{
-		m_dirty.m_depth = false;
-		stateDirty = true;
-		m_hashes.m_depth = computeHash(&m_state.m_depth, sizeof(m_state.m_depth));
-	}
-
-	// Stencil
-	if(m_fbStencil && m_dirty.m_stencil)
-	{
-		m_dirty.m_stencil = false;
-		stateDirty = true;
-		m_hashes.m_stencil = computeHash(&m_state.m_stencil, sizeof(m_state.m_stencil));
-	}
-
-	// Color
-	if(m_fbColorAttachmentCount)
-	{
-		ANKI_ASSERT((m_fbColorAttachmentCount == m_shaderColorAttachmentWritemask.getSetBitCount() || !m_shaderColorAttachmentWritemask)
-					&& "Shader and FB should have same attachment mask or shader mask should be zero");
-
-		if(m_dirty.m_color)
-		{
-			m_dirty.m_color = false;
-			m_hashes.m_color = m_state.m_color.m_alphaToCoverageEnabled ? 1 : 2;
-			stateDirty = true;
-		}
-
-		for(U32 i = 0; i < m_fbColorAttachmentCount; ++i)
-		{
-			if(m_dirty.m_colAttachments.get(i))
-			{
-				m_dirty.m_colAttachments.unset(i);
-				m_hashes.m_colAttachments[i] = computeHash(&m_state.m_color.m_attachments[i], sizeof(m_state.m_color.m_attachments[i]));
-				stateDirty = true;
-			}
-		}
-	}
-
-	// Rpass
-	if(m_dirty.m_rpass)
-	{
-		m_dirty.m_rpass = false;
-		stateDirty = true;
-		m_hashes.m_rpass = computeHash(m_state.m_attachmentFormats.getBegin(), m_state.m_attachmentFormats.getSizeInBytes());
-	}
-
-	return stateDirty;
-}
-
-void PipelineStateTracker::updateSuperHash()
-{
-	Array<U64, sizeof(Hashes) / sizeof(U64)> buff;
-	U count = 0;
-
-	// Prog
-	buff[count++] = m_hashes.m_prog;
-
-	// Rpass
-	buff[count++] = m_hashes.m_rpass;
-
-	// Vertex
-	if(!!m_shaderVertexAttributeMask)
-	{
-		for(VertexAttributeSemantic i : EnumIterable<VertexAttributeSemantic>())
-		{
-			if(m_shaderVertexAttributeMask.get(i))
-			{
-				buff[count++] = m_hashes.m_vertexAttribs[i];
-			}
-		}
-	}
-
-	// IA
-	buff[count++] = m_hashes.m_ia;
-
-	// Rasterizer
-	buff[count++] = m_hashes.m_raster;
-
-	// Depth
-	if(m_fbDepth)
-	{
-		buff[count++] = m_hashes.m_depth;
-	}
-
-	// Stencil
-	if(m_fbStencil)
-	{
-		buff[count++] = m_hashes.m_stencil;
-	}
-
-	// Color
-	if(m_fbColorAttachmentCount)
-	{
-		buff[count++] = m_hashes.m_color;
-
-		for(U i = 0; i < m_fbColorAttachmentCount; ++i)
-		{
-			buff[count++] = m_hashes.m_colAttachments[i];
-		}
-	}
-
-	// Super hash
-	m_hashes.m_superHash = computeHash(&buff[0], count * sizeof(buff[0]));
-}
-
-const VkGraphicsPipelineCreateInfo& PipelineStateTracker::updatePipelineCreateInfo()
-{
-	VkGraphicsPipelineCreateInfo& ci = m_ci.m_ppline;
-
-	ci = {};
-	ci.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
-
-	if(m_pipelineStatisticsEnabled)
-	{
-		ci.flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR;
-	}
-
-	// Prog
-	ci.pStages = static_cast<const ShaderProgramImpl&>(*m_state.m_prog).getShaderCreateInfos(ci.stageCount);
-
-	// Vert
-	VkPipelineVertexInputStateCreateInfo& vertCi = m_ci.m_vert;
-	vertCi = {};
-	vertCi.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
-	vertCi.pVertexAttributeDescriptions = &m_ci.m_attribs[0];
-	vertCi.pVertexBindingDescriptions = &m_ci.m_vertBindings[0];
-
-	BitSet<U32(VertexAttributeSemantic::kCount), U8> bindingSet = {false};
-	for(VertexAttributeSemantic semantic : EnumIterable<VertexAttributeSemantic>())
-	{
-		if(m_shaderVertexAttributeMask.get(semantic))
-		{
-			VkVertexInputAttributeDescription& attrib = m_ci.m_attribs[vertCi.vertexAttributeDescriptionCount++];
-			attrib.binding = m_state.m_vertex.m_attributes[semantic].m_binding;
-			attrib.format = convertFormat(m_state.m_vertex.m_attributes[semantic].m_format);
-			attrib.location = m_semanticToVertexAttributeLocation[semantic];
-			attrib.offset = U32(m_state.m_vertex.m_attributes[semantic].m_offset);
-
-			if(!bindingSet.get(attrib.binding))
-			{
-				bindingSet.set(attrib.binding);
-
-				VkVertexInputBindingDescription& binding = m_ci.m_vertBindings[vertCi.vertexBindingDescriptionCount++];
-
-				binding.binding = attrib.binding;
-				binding.inputRate = convertVertexStepRate(m_state.m_vertex.m_bindings[attrib.binding].m_stepRate);
-				binding.stride = m_state.m_vertex.m_bindings[attrib.binding].m_stride;
-			}
-		}
-	}
-
-	ci.pVertexInputState = &vertCi;
-
-	// IA
-	VkPipelineInputAssemblyStateCreateInfo& iaCi = m_ci.m_ia;
-	iaCi = {};
-	iaCi.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
-	iaCi.primitiveRestartEnable = m_state.m_inputAssembler.m_primitiveRestartEnabled;
-	iaCi.topology = convertTopology(m_state.m_inputAssembler.m_topology);
-	ci.pInputAssemblyState = &iaCi;
-
-	// Viewport
-	VkPipelineViewportStateCreateInfo& vpCi = m_ci.m_vp;
-	vpCi = {};
-	vpCi.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
-	vpCi.scissorCount = 1;
-	vpCi.viewportCount = 1;
-	ci.pViewportState = &vpCi;
-
-	// Raster
-	VkPipelineRasterizationStateCreateInfo& rastCi = m_ci.m_rast;
-	rastCi = {};
-	rastCi.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
-	rastCi.depthClampEnable = false;
-	rastCi.rasterizerDiscardEnable = false;
-	rastCi.polygonMode = convertFillMode(m_state.m_rasterizer.m_fillMode);
-	rastCi.cullMode = convertCullMode(m_state.m_rasterizer.m_cullMode);
-	rastCi.frontFace = (!m_rendersToSwapchain) ? VK_FRONT_FACE_CLOCKWISE : VK_FRONT_FACE_COUNTER_CLOCKWISE; // For viewport flip
-	rastCi.depthBiasEnable = m_state.m_rasterizer.m_depthBiasEnabled;
-	rastCi.lineWidth = 1.0;
-	ci.pRasterizationState = &rastCi;
-
-	if(m_state.m_rasterizer.m_rasterizationOrder != RasterizationOrder::kOrdered)
-	{
-		VkPipelineRasterizationStateRasterizationOrderAMD& rastOrderCi = m_ci.m_rasterOrder;
-		rastOrderCi = {};
-		rastOrderCi.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_RASTERIZATION_ORDER_AMD;
-		rastOrderCi.rasterizationOrder = convertRasterizationOrder(m_state.m_rasterizer.m_rasterizationOrder);
-
-		appendPNextList(rastCi, &rastOrderCi);
-	}
-
-	// MS
-	VkPipelineMultisampleStateCreateInfo& msCi = m_ci.m_ms;
-	msCi = {};
-	msCi.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
-	msCi.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
-	ci.pMultisampleState = &msCi;
-
-	// DS
-	if(m_fbDepth || m_fbStencil)
-	{
-		VkPipelineDepthStencilStateCreateInfo& dsCi = m_ci.m_ds;
-		dsCi = {};
-		dsCi.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
-
-		if(m_fbDepth)
-		{
-			dsCi.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
-			dsCi.depthTestEnable = m_state.m_depth.m_depthCompareFunction != CompareOperation::kAlways || m_state.m_depth.m_depthWriteEnabled;
-			dsCi.depthWriteEnable = m_state.m_depth.m_depthWriteEnabled;
-			dsCi.depthCompareOp = convertCompareOp(m_state.m_depth.m_depthCompareFunction);
-		}
-
-		if(m_fbStencil)
-		{
-			const StencilPipelineState& ss = m_state.m_stencil;
-
-			dsCi.stencilTestEnable = !stencilTestDisabled(ss.m_face[0].m_stencilFailOperation, ss.m_face[0].m_stencilPassDepthFailOperation,
-														  ss.m_face[0].m_stencilPassDepthPassOperation, ss.m_face[0].m_compareFunction)
-									 || !stencilTestDisabled(ss.m_face[1].m_stencilFailOperation, ss.m_face[1].m_stencilPassDepthFailOperation,
-															 ss.m_face[1].m_stencilPassDepthPassOperation, ss.m_face[1].m_compareFunction);
-
-			dsCi.front.failOp = convertStencilOp(ss.m_face[0].m_stencilFailOperation);
-			dsCi.front.passOp = convertStencilOp(ss.m_face[0].m_stencilPassDepthPassOperation);
-			dsCi.front.depthFailOp = convertStencilOp(ss.m_face[0].m_stencilPassDepthFailOperation);
-			dsCi.front.compareOp = convertCompareOp(ss.m_face[0].m_compareFunction);
-			dsCi.back.failOp = convertStencilOp(ss.m_face[1].m_stencilFailOperation);
-			dsCi.back.passOp = convertStencilOp(ss.m_face[1].m_stencilPassDepthPassOperation);
-			dsCi.back.depthFailOp = convertStencilOp(ss.m_face[1].m_stencilPassDepthFailOperation);
-			dsCi.back.compareOp = convertCompareOp(ss.m_face[1].m_compareFunction);
-		}
-
-		ci.pDepthStencilState = &dsCi;
-	}
-
-	// Color/blend
-	if(m_fbColorAttachmentCount)
-	{
-		VkPipelineColorBlendStateCreateInfo& colCi = m_ci.m_color;
-		colCi = {};
-		colCi.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
-		colCi.attachmentCount = m_fbColorAttachmentCount;
-		colCi.pAttachments = &m_ci.m_colAttachments[0];
-
-		for(U i = 0; i < colCi.attachmentCount; ++i)
-		{
-			VkPipelineColorBlendAttachmentState& out = m_ci.m_colAttachments[i];
-			const ColorAttachmentState& in = m_state.m_color.m_attachments[i];
-
-			out.blendEnable = !blendingDisabled(in.m_srcBlendFactorRgb, in.m_dstBlendFactorRgb, in.m_srcBlendFactorA, in.m_dstBlendFactorA,
-												in.m_blendFunctionRgb, in.m_blendFunctionA);
-			out.srcColorBlendFactor = convertBlendFactor(in.m_srcBlendFactorRgb);
-			out.dstColorBlendFactor = convertBlendFactor(in.m_dstBlendFactorRgb);
-			out.srcAlphaBlendFactor = convertBlendFactor(in.m_srcBlendFactorA);
-			out.dstAlphaBlendFactor = convertBlendFactor(in.m_dstBlendFactorA);
-			out.colorBlendOp = convertBlendOperation(in.m_blendFunctionRgb);
-			out.alphaBlendOp = convertBlendOperation(in.m_blendFunctionA);
-
-			out.colorWriteMask = convertColorWriteMask(in.m_channelWriteMask);
-		}
-
-		ci.pColorBlendState = &colCi;
-	}
-
-	// Dyn state
-	VkPipelineDynamicStateCreateInfo& dynCi = m_ci.m_dyn;
-	dynCi = {};
-	dynCi.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
-
-	// Renderpass related (Dynamic rendering)
-	VkPipelineRenderingCreateInfoKHR& dynRendering = m_ci.m_dynamicRendering;
-	dynRendering = {};
-	dynRendering.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR;
-	dynRendering.colorAttachmentCount = m_fbColorAttachmentCount;
-	dynRendering.pColorAttachmentFormats = m_ci.m_dynamicRenderingAttachmentFormats.getBegin();
-	for(U i = 0; i < m_fbColorAttachmentCount; ++i)
-	{
-		m_ci.m_dynamicRenderingAttachmentFormats[i] = convertFormat(m_state.m_attachmentFormats[i]);
-	}
-
-	if(m_fbDepth)
-	{
-		dynRendering.depthAttachmentFormat = convertFormat(m_state.m_attachmentFormats[kMaxColorRenderTargets]);
-	}
-
-	if(m_fbStencil)
-	{
-		dynRendering.stencilAttachmentFormat = convertFormat(m_state.m_attachmentFormats[kMaxColorRenderTargets]);
-	}
-
-	appendPNextList(ci, &m_ci.m_dynamicRendering);
-
-	// Almost all state is dynamic. Depth bias is static
-	static constexpr Array<VkDynamicState, 10> kDyn = {
-		{VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, VK_DYNAMIC_STATE_BLEND_CONSTANTS, VK_DYNAMIC_STATE_DEPTH_BOUNDS,
-		 VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE, VK_DYNAMIC_STATE_LINE_WIDTH,
-		 VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_FRAGMENT_SHADING_RATE_KHR}};
-
-	dynCi.dynamicStateCount = (m_vrsCapable) ? kDyn.getSize() : (kDyn.getSize() - 1);
-	dynCi.pDynamicStates = &kDyn[0];
-	ci.pDynamicState = &dynCi;
-
-	// The rest
-	ci.layout = static_cast<const ShaderProgramImpl&>(*m_state.m_prog).getPipelineLayout().getHandle();
-	ci.subpass = 0;
-
-	return ci;
-}
-
-class PipelineFactory::PipelineInternal
-{
-public:
-	VkPipeline m_handle = VK_NULL_HANDLE;
-};
-
-class PipelineFactory::Hasher
-{
-public:
-	U64 operator()(U64 h)
-	{
-		return h;
-	}
-};
-
-PipelineFactory::PipelineFactory()
-{
-}
-
-PipelineFactory::~PipelineFactory()
-{
-}
-
-void PipelineFactory::destroy()
-{
-	for(auto it : m_pplines)
-	{
-		if(it.m_handle)
-		{
-			vkDestroyPipeline(getVkDevice(), it.m_handle, nullptr);
-		}
-	}
-
-	m_pplines.destroy();
-}
-
-void PipelineFactory::getOrCreatePipeline(PipelineStateTracker& state, Pipeline& ppline, Bool& stateDirty)
-{
-	ANKI_TRACE_SCOPED_EVENT(VkPipelineGetOrCreate);
-
-	U64 hash;
-	state.flush(hash, stateDirty);
-
-	if(!stateDirty) [[unlikely]]
-	{
-		ppline.m_handle = VK_NULL_HANDLE;
-		return;
-	}
-
-	// Check if ppline exists
-	{
-		RLockGuard<RWMutex> lock(m_pplinesMtx);
-		auto it = m_pplines.find(hash);
-		if(it != m_pplines.getEnd())
-		{
-			ppline.m_handle = (*it).m_handle;
-			ANKI_TRACE_INC_COUNTER(VkPipelineCacheHit, 1);
-			return;
-		}
-	}
-
-	// Doesnt exist. Need to create it
-
-	WLockGuard<RWMutex> lock(m_pplinesMtx);
-
-	// Check again
-	auto it = m_pplines.find(hash);
-	if(it != m_pplines.getEnd())
-	{
-		ppline.m_handle = (*it).m_handle;
-		return;
-	}
-
-	// Create it for real
-	PipelineInternal pp;
-	const VkGraphicsPipelineCreateInfo& ci = state.updatePipelineCreateInfo();
-
-	{
-		ANKI_TRACE_SCOPED_EVENT(VkPipelineCreate);
-
-#if ANKI_PLATFORM_MOBILE
-		if(PipelineCache::getSingleton().m_globalCreatePipelineMtx)
-		{
-			PipelineCache::getSingleton().m_globalCreatePipelineMtx->lock();
-		}
-#endif
-
-		ANKI_VK_CHECKF(vkCreateGraphicsPipelines(getVkDevice(), PipelineCache::getSingleton().m_cacheHandle, 1, &ci, nullptr, &pp.m_handle));
-
-#if ANKI_PLATFORM_MOBILE
-		if(PipelineCache::getSingleton().m_globalCreatePipelineMtx)
-		{
-			PipelineCache::getSingleton().m_globalCreatePipelineMtx->unlock();
-		}
-#endif
-	}
-
-	ANKI_TRACE_INC_COUNTER(VkPipelineCacheMiss, 1);
-
-	m_pplines.emplace(hash, pp);
-	ppline.m_handle = pp.m_handle;
-
-	// Print shader info
-	getGrManagerImpl().printPipelineShaderInfo(pp.m_handle, state.m_state.m_prog->getName(), hash);
-}
-
-Error PipelineCache::init(CString cacheDir)
-{
-	ANKI_ASSERT(cacheDir);
-	m_dumpSize = g_diskShaderCacheMaxSizeCVar.get();
-	m_dumpFilename.sprintf("%s/VkPipelineCache", cacheDir.cstr());
-
-	// Try read the pipeline cache file.
-	GrDynamicArray<U8, PtrSize> diskDump;
-	if(fileExists(m_dumpFilename.toCString()))
-	{
-		File file;
-		ANKI_CHECK(file.open(m_dumpFilename.toCString(), FileOpenFlag::kBinary | FileOpenFlag::kRead));
-
-		const PtrSize diskDumpSize = file.getSize();
-		if(diskDumpSize <= sizeof(U8) * VK_UUID_SIZE)
-		{
-			ANKI_VK_LOGI("Pipeline cache dump appears to be empty: %s", &m_dumpFilename[0]);
-		}
-		else
-		{
-			// Get current pipeline UUID and compare it with the cache's
-			VkPhysicalDeviceProperties props;
-			vkGetPhysicalDeviceProperties(getGrManagerImpl().getPhysicalDevice(), &props);
-
-			Array<U8, VK_UUID_SIZE> cacheUuid;
-			ANKI_CHECK(file.read(&cacheUuid[0], VK_UUID_SIZE));
-
-			if(memcmp(&cacheUuid[0], &props.pipelineCacheUUID[0], VK_UUID_SIZE) != 0)
-			{
-				ANKI_VK_LOGI("Pipeline cache dump is not compatible with the current device: %s", &m_dumpFilename[0]);
-			}
-			else
-			{
-				diskDump.resize(diskDumpSize - VK_UUID_SIZE);
-				ANKI_CHECK(file.read(&diskDump[0], diskDumpSize - VK_UUID_SIZE));
-			}
-		}
-	}
-	else
-	{
-		ANKI_VK_LOGI("Pipeline cache dump not found: %s", &m_dumpFilename[0]);
-	}
-
-	// Create the cache
-	VkPipelineCacheCreateInfo ci = {};
-	ci.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
-	if(diskDump.getSize())
-	{
-		ANKI_VK_LOGI("Will load %zu bytes of pipeline cache", diskDump.getSize());
-		ci.initialDataSize = diskDump.getSize();
-		ci.pInitialData = &diskDump[0];
-	}
-
-	ANKI_VK_CHECK(vkCreatePipelineCache(getVkDevice(), &ci, nullptr, &m_cacheHandle));
-
-#if ANKI_PLATFORM_MOBILE
-	ANKI_ASSERT(GrManager::getSingleton().getDeviceCapabilities() != GpuVendor::kNone);
-	if(GrManager::getSingleton().getDeviceCapabilities().m_gpuVendor == GpuVendor::kQualcomm)
-	{
-		// Calling vkCreateGraphicsPipeline from multiple threads crashes qualcomm's compiler
-		ANKI_VK_LOGI("Enabling workaround for vkCreateGraphicsPipeline crashing when called from multiple threads");
-		m_globalCreatePipelineMtx = anki::newInstance<Mutex>(GrMemoryPool::getSingleton());
-	}
-#endif
-
-	return Error::kNone;
-}
-
-void PipelineCache::destroy()
-{
-	const Error err = destroyInternal();
-	if(err)
-	{
-		ANKI_VK_LOGE("An error occurred while storing the pipeline cache to disk. Will ignore");
-	}
-
-	m_dumpFilename.destroy();
-}
-
-Error PipelineCache::destroyInternal()
-{
-#if ANKI_PLATFORM_MOBILE
-	deleteInstance(GrMemoryPool::getSingleton(), m_globalCreatePipelineMtx);
-#endif
-
-	if(m_cacheHandle)
-	{
-		// Get size of cache
-		size_t size = 0;
-		ANKI_VK_CHECK(vkGetPipelineCacheData(getVkDevice(), m_cacheHandle, &size, nullptr));
-		size = min(size, m_dumpSize);
-
-		if(size > 0)
-		{
-			// Read cache
-			GrDynamicArray<U8, PtrSize> cacheData;
-			cacheData.resize(size);
-			ANKI_VK_CHECK(vkGetPipelineCacheData(getVkDevice(), m_cacheHandle, &size, &cacheData[0]));
-
-			// Write file
-			File file;
-			ANKI_CHECK(file.open(&m_dumpFilename[0], FileOpenFlag::kBinary | FileOpenFlag::kWrite));
-
-			VkPhysicalDeviceProperties props;
-			vkGetPhysicalDeviceProperties(getGrManagerImpl().getPhysicalDevice(), &props);
-
-			ANKI_CHECK(file.write(&props.pipelineCacheUUID[0], VK_UUID_SIZE));
-			ANKI_CHECK(file.write(&cacheData[0], size));
-
-			ANKI_VK_LOGI("Dumped %zu bytes of the pipeline cache", size);
-		}
-
-		// Destroy cache
-		vkDestroyPipelineCache(getVkDevice(), m_cacheHandle, nullptr);
-		m_cacheHandle = VK_NULL_HANDLE;
-	}
-
-	return Error::kNone;
-}
-
-} // end namespace anki

+ 0 - 602
AnKi/Gr/Vulkan/VkPipelineFactory.h

@@ -1,602 +0,0 @@
-// Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#pragma once
-
-#include <AnKi/Gr/ShaderProgram.h>
-#include <AnKi/Gr/Vulkan/VkShaderProgram.h>
-#include <AnKi/Util/HashMap.h>
-
-namespace anki {
-
-/// @addtogroup vulkan
-/// @{
-
-class VertexBufferBindingPipelineState
-{
-public:
-	U8 m_stride = kMaxU8; ///< Vertex stride.
-	VertexStepRate m_stepRate = VertexStepRate::kVertex;
-
-	Bool operator==(const VertexBufferBindingPipelineState& b) const
-	{
-		return m_stride == b.m_stride && m_stepRate == b.m_stepRate;
-	}
-
-	Bool operator!=(const VertexBufferBindingPipelineState& b) const
-	{
-		return !(*this == b);
-	}
-};
-static_assert(sizeof(VertexBufferBindingPipelineState) == sizeof(U16), "Packed because it will be hashed");
-
-class VertexAttributeBindingPipelineState
-{
-public:
-	PtrSize m_offset = 0;
-	Format m_format = Format::kNone;
-	U8 m_binding = 0;
-	Array<U8, 3> m_padding = {};
-
-	Bool operator==(const VertexAttributeBindingPipelineState& b) const
-	{
-		return m_format == b.m_format && m_offset == b.m_offset && m_binding == b.m_binding;
-	}
-
-	Bool operator!=(const VertexAttributeBindingPipelineState& b) const
-	{
-		return !(*this == b);
-	}
-};
-static_assert(sizeof(VertexAttributeBindingPipelineState) == 2 * sizeof(PtrSize), "Packed because it will be hashed");
-
-class VertexPipelineState
-{
-public:
-	Array<VertexBufferBindingPipelineState, U32(VertexAttributeSemantic::kCount)> m_bindings;
-	Array<VertexAttributeBindingPipelineState, U32(VertexAttributeSemantic::kCount)> m_attributes;
-};
-static_assert(sizeof(VertexPipelineState)
-				  == sizeof(VertexBufferBindingPipelineState) * U32(VertexAttributeSemantic::kCount)
-						 + sizeof(VertexAttributeBindingPipelineState) * U32(VertexAttributeSemantic::kCount),
-			  "Packed because it will be hashed");
-
-class InputAssemblerPipelineState
-{
-public:
-	PrimitiveTopology m_topology = PrimitiveTopology::kTriangles;
-	Bool m_primitiveRestartEnabled = false;
-};
-static_assert(sizeof(InputAssemblerPipelineState) == sizeof(U8) * 2, "Packed because it will be hashed");
-
-class RasterizerPipelineState
-{
-public:
-	FillMode m_fillMode = FillMode::kSolid;
-	FaceSelectionBit m_cullMode = FaceSelectionBit::kBack;
-	RasterizationOrder m_rasterizationOrder = RasterizationOrder::kOrdered;
-	Bool m_depthBiasEnabled = false;
-};
-static_assert(sizeof(RasterizerPipelineState) == sizeof(U32), "Packed because it will be hashed");
-
-class DepthPipelineState
-{
-public:
-	Bool m_depthWriteEnabled = true;
-	CompareOperation m_depthCompareFunction = CompareOperation::kLess;
-};
-static_assert(sizeof(DepthPipelineState) == sizeof(U8) * 2, "Packed because it will be hashed");
-
-class StencilPipelineState
-{
-public:
-	class S
-	{
-	public:
-		StencilOperation m_stencilFailOperation = StencilOperation::kKeep;
-		StencilOperation m_stencilPassDepthFailOperation = StencilOperation::kKeep;
-		StencilOperation m_stencilPassDepthPassOperation = StencilOperation::kKeep;
-		CompareOperation m_compareFunction = CompareOperation::kAlways;
-	};
-
-	Array<S, 2> m_face;
-};
-static_assert(sizeof(StencilPipelineState) == sizeof(U32) * 2, "Packed because it will be hashed");
-
-class ColorAttachmentState
-{
-public:
-	BlendFactor m_srcBlendFactorRgb = BlendFactor::kOne;
-	BlendFactor m_srcBlendFactorA = BlendFactor::kOne;
-	BlendFactor m_dstBlendFactorRgb = BlendFactor::kZero;
-	BlendFactor m_dstBlendFactorA = BlendFactor::kZero;
-	BlendOperation m_blendFunctionRgb = BlendOperation::kAdd;
-	BlendOperation m_blendFunctionA = BlendOperation::kAdd;
-	ColorBit m_channelWriteMask = ColorBit::kAll;
-};
-static_assert(sizeof(ColorAttachmentState) == sizeof(U8) * 7, "Packed because it will be hashed");
-
-class ColorPipelineState
-{
-public:
-	Bool m_alphaToCoverageEnabled = false;
-	Array<ColorAttachmentState, kMaxColorRenderTargets> m_attachments;
-};
-static_assert(sizeof(ColorPipelineState) == sizeof(ColorAttachmentState) * kMaxColorRenderTargets + sizeof(U8), "Packed because it will be hashed");
-
-class AllPipelineState
-{
-public:
-	const ShaderProgramImpl* m_prog = nullptr;
-	Array<Format, kMaxColorRenderTargets + 1> m_attachmentFormats = {};
-
-	VertexPipelineState m_vertex;
-	InputAssemblerPipelineState m_inputAssembler;
-	RasterizerPipelineState m_rasterizer;
-	DepthPipelineState m_depth;
-	StencilPipelineState m_stencil;
-	ColorPipelineState m_color;
-
-	void reset()
-	{
-		::new(this) AllPipelineState();
-	}
-};
-
-/// Track changes in the static state.
-class PipelineStateTracker
-{
-	friend class PipelineFactory;
-
-public:
-	PipelineStateTracker()
-	{
-	}
-
-	PipelineStateTracker(const PipelineStateTracker&) = delete; // Non-copyable
-
-	PipelineStateTracker& operator=(const PipelineStateTracker&) = delete; // Non-copyable
-
-	void bindVertexBuffer(U32 binding, U32 stride, VertexStepRate stepRate)
-	{
-		VertexBufferBindingPipelineState b;
-		ANKI_ASSERT(stride <= kMaxU8);
-		b.m_stride = U8(stride);
-		b.m_stepRate = stepRate;
-		if(m_state.m_vertex.m_bindings[binding] != b)
-		{
-			m_state.m_vertex.m_bindings[binding] = b;
-			m_dirty.m_vertBindings.set(binding);
-		}
-		m_set.m_vertBindings.set(binding);
-	}
-
-	void setVertexAttribute(VertexAttributeSemantic semantic, U32 buffBinding, const Format fmt, U32 relativeOffset)
-	{
-		VertexAttributeBindingPipelineState b;
-		b.m_binding = U8(buffBinding);
-		b.m_format = fmt;
-		b.m_offset = relativeOffset;
-		if(m_state.m_vertex.m_attributes[semantic] != b)
-		{
-			m_state.m_vertex.m_attributes[semantic] = b;
-			m_dirty.m_attribs.set(semantic);
-		}
-		m_set.m_attribs.set(semantic);
-	}
-
-	void setPrimitiveRestart(Bool enable)
-	{
-		if(m_state.m_inputAssembler.m_primitiveRestartEnabled != enable)
-		{
-			m_state.m_inputAssembler.m_primitiveRestartEnabled = enable;
-			m_dirty.m_inputAssembler = true;
-		}
-	}
-
-	void setFillMode(FillMode mode)
-	{
-		if(m_state.m_rasterizer.m_fillMode != mode)
-		{
-			m_state.m_rasterizer.m_fillMode = mode;
-			m_dirty.m_rasterizer = true;
-		}
-	}
-
-	void setCullMode(FaceSelectionBit mode)
-	{
-		if(m_state.m_rasterizer.m_cullMode != mode)
-		{
-			m_state.m_rasterizer.m_cullMode = mode;
-			m_dirty.m_rasterizer = true;
-		}
-	}
-
-	void setPolygonOffset(F32 factor, F32 units)
-	{
-		const Bool depthBiasEnabled = factor != 0.0f || units != 0.0f;
-		if(depthBiasEnabled != m_state.m_rasterizer.m_depthBiasEnabled)
-		{
-			m_state.m_rasterizer.m_depthBiasEnabled = depthBiasEnabled;
-			m_dirty.m_rasterizer = true;
-		}
-	}
-
-	void setRasterizationOrder(RasterizationOrder order)
-	{
-		if(m_state.m_rasterizer.m_rasterizationOrder != order)
-		{
-			m_state.m_rasterizer.m_rasterizationOrder = order;
-			m_dirty.m_rasterizer = true;
-		}
-	}
-
-	void setStencilOperations(FaceSelectionBit face, StencilOperation stencilFail, StencilOperation stencilPassDepthFail,
-							  StencilOperation stencilPassDepthPass)
-	{
-		if(!!(face & FaceSelectionBit::kFront)
-		   && (m_state.m_stencil.m_face[0].m_stencilFailOperation != stencilFail
-			   || m_state.m_stencil.m_face[0].m_stencilPassDepthFailOperation != stencilPassDepthFail
-			   || m_state.m_stencil.m_face[0].m_stencilPassDepthPassOperation != stencilPassDepthPass))
-		{
-			m_state.m_stencil.m_face[0].m_stencilFailOperation = stencilFail;
-			m_state.m_stencil.m_face[0].m_stencilPassDepthFailOperation = stencilPassDepthFail;
-			m_state.m_stencil.m_face[0].m_stencilPassDepthPassOperation = stencilPassDepthPass;
-			m_dirty.m_stencil = true;
-		}
-
-		if(!!(face & FaceSelectionBit::kBack)
-		   && (m_state.m_stencil.m_face[1].m_stencilFailOperation != stencilFail
-			   || m_state.m_stencil.m_face[1].m_stencilPassDepthFailOperation != stencilPassDepthFail
-			   || m_state.m_stencil.m_face[1].m_stencilPassDepthPassOperation != stencilPassDepthPass))
-		{
-			m_state.m_stencil.m_face[1].m_stencilFailOperation = stencilFail;
-			m_state.m_stencil.m_face[1].m_stencilPassDepthFailOperation = stencilPassDepthFail;
-			m_state.m_stencil.m_face[1].m_stencilPassDepthPassOperation = stencilPassDepthPass;
-			m_dirty.m_stencil = true;
-		}
-	}
-
-	void setStencilCompareOperation(FaceSelectionBit face, CompareOperation comp)
-	{
-		if(!!(face & FaceSelectionBit::kFront) && m_state.m_stencil.m_face[0].m_compareFunction != comp)
-		{
-			m_state.m_stencil.m_face[0].m_compareFunction = comp;
-			m_dirty.m_stencil = true;
-		}
-
-		if(!!(face & FaceSelectionBit::kBack) && m_state.m_stencil.m_face[1].m_compareFunction != comp)
-		{
-			m_state.m_stencil.m_face[1].m_compareFunction = comp;
-			m_dirty.m_stencil = true;
-		}
-	}
-
-	void setDepthWrite(Bool enable)
-	{
-		if(m_state.m_depth.m_depthWriteEnabled != enable)
-		{
-			m_state.m_depth.m_depthWriteEnabled = enable;
-			m_dirty.m_depth = true;
-		}
-	}
-
-	void setDepthCompareOperation(CompareOperation op)
-	{
-		if(m_state.m_depth.m_depthCompareFunction != op)
-		{
-			m_state.m_depth.m_depthCompareFunction = op;
-			m_dirty.m_depth = true;
-		}
-	}
-
-	void setAlphaToCoverage(Bool enable)
-	{
-		if(m_state.m_color.m_alphaToCoverageEnabled != enable)
-		{
-			m_state.m_color.m_alphaToCoverageEnabled = enable;
-			m_dirty.m_color = true;
-		}
-	}
-
-	void setColorChannelWriteMask(U32 attachment, ColorBit mask)
-	{
-		if(m_state.m_color.m_attachments[attachment].m_channelWriteMask != mask)
-		{
-			m_state.m_color.m_attachments[attachment].m_channelWriteMask = mask;
-			m_dirty.m_colAttachments.set(attachment);
-		}
-	}
-
-	void setBlendFactors(U32 attachment, BlendFactor srcRgb, BlendFactor dstRgb, BlendFactor srcA, BlendFactor dstA)
-	{
-		ColorAttachmentState& c = m_state.m_color.m_attachments[attachment];
-		if(c.m_srcBlendFactorRgb != srcRgb || c.m_dstBlendFactorRgb != dstRgb || c.m_srcBlendFactorA != srcA || c.m_dstBlendFactorA != dstA)
-		{
-			c.m_srcBlendFactorRgb = srcRgb;
-			c.m_dstBlendFactorRgb = dstRgb;
-			c.m_srcBlendFactorA = srcA;
-			c.m_dstBlendFactorA = dstA;
-			m_dirty.m_colAttachments.set(attachment);
-		}
-	}
-
-	void setBlendOperation(U32 attachment, BlendOperation funcRgb, BlendOperation funcA)
-	{
-		ColorAttachmentState& c = m_state.m_color.m_attachments[attachment];
-		if(c.m_blendFunctionRgb != funcRgb || c.m_blendFunctionA != funcA)
-		{
-			c.m_blendFunctionRgb = funcRgb;
-			c.m_blendFunctionA = funcA;
-			m_dirty.m_colAttachments.set(attachment);
-		}
-	}
-
-	void bindShaderProgram(const ShaderProgramImpl* prog)
-	{
-		if(prog != m_state.m_prog)
-		{
-			m_shaderColorAttachmentWritemask = prog->getReflectionInfo().m_fragment.m_colorAttachmentWritemask;
-
-			if(!!(prog->getShaderTypes() & ShaderTypeBit::kVertex))
-			{
-				m_shaderVertexAttributeMask = prog->getReflectionInfo().m_vertex.m_vertexAttributeMask;
-				m_semanticToVertexAttributeLocation = prog->getReflectionInfo().m_vertex.m_vertexAttributeLocations;
-			}
-
-			m_state.m_prog = prog;
-			m_dirty.m_prog = true;
-		}
-	}
-
-	void beginRenderPass(ConstWeakArray<Format> colorFormats, Format depthStencilFormat, Bool rendersToSwapchain)
-	{
-		zeroMemory(m_state.m_attachmentFormats);
-		m_fbColorAttachmentCount = U8(colorFormats.getSize());
-
-		for(U32 i = 0; i < colorFormats.getSize(); ++i)
-		{
-			m_state.m_attachmentFormats[i] = colorFormats[i];
-		}
-
-		m_state.m_attachmentFormats[kMaxColorRenderTargets] = depthStencilFormat;
-
-		if(depthStencilFormat != Format::kNone)
-		{
-			const FormatInfo& inf = getFormatInfo(depthStencilFormat);
-			ANKI_ASSERT(!!inf.m_depthStencil);
-			m_fbDepth = !!(inf.m_depthStencil & DepthStencilAspectBit::kDepth);
-			m_fbStencil = !!(inf.m_depthStencil & DepthStencilAspectBit::kStencil);
-		}
-		else
-		{
-			m_fbDepth = false;
-			m_fbStencil = false;
-		}
-
-		m_rendersToSwapchain = rendersToSwapchain;
-		m_dirty.m_rpass = true;
-	}
-
-	void endRenderPass()
-	{
-	}
-
-	void setPrimitiveTopology(PrimitiveTopology topology)
-	{
-		if(m_state.m_inputAssembler.m_topology != topology)
-		{
-			m_state.m_inputAssembler.m_topology = topology;
-			m_dirty.m_inputAssembler = true;
-		}
-	}
-
-	PrimitiveTopology getPrimitiveTopology() const
-	{
-		return m_state.m_inputAssembler.m_topology;
-	}
-
-	Bool getEnablePipelineStatistics() const
-	{
-		return m_pipelineStatisticsEnabled;
-	}
-
-	void setEnablePipelineStatistics(Bool enable)
-	{
-		m_pipelineStatisticsEnabled = enable;
-	}
-
-	void setVrsCapable(Bool capable)
-	{
-		m_vrsCapable = capable;
-	}
-
-	/// Flush state
-	void flush(U64& pipelineHash, Bool& stateDirty)
-	{
-		const Bool dirtyHashes = updateHashes();
-		if(dirtyHashes)
-		{
-			updateSuperHash();
-		}
-
-		if(m_hashes.m_superHash != m_hashes.m_lastSuperHash)
-		{
-			m_hashes.m_lastSuperHash = m_hashes.m_superHash;
-			stateDirty = true;
-		}
-		else
-		{
-			stateDirty = false;
-		}
-
-		pipelineHash = m_hashes.m_superHash;
-		ANKI_ASSERT(pipelineHash);
-	}
-
-	/// Populate the internal pipeline create info structure.
-	const VkGraphicsPipelineCreateInfo& updatePipelineCreateInfo();
-
-	void reset();
-
-private:
-	AllPipelineState m_state;
-
-	class DirtyBits
-	{
-	public:
-		Bool m_prog : 1 = true;
-		Bool m_rpass : 1 = true;
-		Bool m_inputAssembler : 1 = true;
-		Bool m_rasterizer : 1 = true;
-		Bool m_depth : 1 = true;
-		Bool m_stencil : 1 = true;
-		Bool m_color : 1 = true;
-
-		// Vertex
-		BitSet<U32(VertexAttributeSemantic::kCount), U8> m_attribs = {true};
-		BitSet<U32(VertexAttributeSemantic::kCount), U8> m_vertBindings = {true};
-
-		BitSet<kMaxColorRenderTargets, U8> m_colAttachments = {true};
-	} m_dirty;
-
-	class SetBits
-	{
-	public:
-		BitSet<U32(VertexAttributeSemantic::kCount), U8> m_attribs = {false};
-		BitSet<U32(VertexAttributeSemantic::kCount), U8> m_vertBindings = {false};
-	} m_set;
-
-	// Shader info
-	BitSet<U32(VertexAttributeSemantic::kCount), U8> m_shaderVertexAttributeMask = {false};
-	BitSet<kMaxColorRenderTargets, U8> m_shaderColorAttachmentWritemask = {false};
-	Array<U8, U32(VertexAttributeSemantic::kCount)> m_semanticToVertexAttributeLocation;
-
-	// Renderpass info
-	Bool m_fbDepth : 1 = false;
-	Bool m_fbStencil : 1 = false;
-	Bool m_rendersToSwapchain : 1 = false;
-	U8 m_fbColorAttachmentCount : 4 = 0;
-
-	Bool m_pipelineStatisticsEnabled : 1 = false;
-	Bool m_vrsCapable : 1 = false;
-
-	class Hashes
-	{
-	public:
-		U64 m_prog;
-		U64 m_rpass;
-		Array<U64, U32(VertexAttributeSemantic::kCount)> m_vertexAttribs;
-		U64 m_ia;
-		U64 m_raster;
-		U64 m_depth;
-		U64 m_stencil;
-		U64 m_color;
-		Array<U64, kMaxColorRenderTargets> m_colAttachments;
-
-		U64 m_superHash;
-		U64 m_lastSuperHash;
-
-		Hashes()
-		{
-			zeroMemory(*this);
-		}
-	} m_hashes;
-
-	// Create info
-	class CreateInfo
-	{
-	public:
-		Array<VkVertexInputBindingDescription, U32(VertexAttributeSemantic::kCount)> m_vertBindings;
-		Array<VkVertexInputAttributeDescription, U32(VertexAttributeSemantic::kCount)> m_attribs;
-		VkPipelineVertexInputStateCreateInfo m_vert;
-		VkPipelineInputAssemblyStateCreateInfo m_ia;
-		VkPipelineViewportStateCreateInfo m_vp;
-		VkPipelineTessellationStateCreateInfo m_tess;
-		VkPipelineRasterizationStateCreateInfo m_rast;
-		VkPipelineMultisampleStateCreateInfo m_ms;
-		VkPipelineDepthStencilStateCreateInfo m_ds;
-		Array<VkPipelineColorBlendAttachmentState, kMaxColorRenderTargets> m_colAttachments;
-		VkPipelineColorBlendStateCreateInfo m_color;
-		VkPipelineDynamicStateCreateInfo m_dyn;
-		VkGraphicsPipelineCreateInfo m_ppline;
-		VkPipelineRasterizationStateRasterizationOrderAMD m_rasterOrder;
-		VkPipelineRenderingCreateInfoKHR m_dynamicRendering;
-		Array<VkFormat, kMaxColorRenderTargets> m_dynamicRenderingAttachmentFormats; ///< Because we can have them living on the stack.
-
-		CreateInfo()
-		{
-			// Do nothing
-		}
-	} m_ci;
-
-	Bool updateHashes();
-	void updateSuperHash();
-};
-
-/// Small wrapper on top of the pipeline.
-class Pipeline
-{
-	friend class PipelineFactory;
-
-public:
-	VkPipeline getHandle() const
-	{
-		ANKI_ASSERT(m_handle);
-		return m_handle;
-	}
-
-private:
-	VkPipeline m_handle ANKI_DEBUG_CODE(= 0);
-};
-
-/// Given some state it creates/hashes pipelines.
-class PipelineFactory
-{
-public:
-	PipelineFactory();
-
-	~PipelineFactory();
-
-	void destroy();
-
-	/// @note Thread-safe.
-	void getOrCreatePipeline(PipelineStateTracker& state, Pipeline& ppline, Bool& stateDirty);
-
-private:
-	class PipelineInternal;
-	class Hasher;
-
-	GrHashMap<U64, PipelineInternal, Hasher> m_pplines;
-	RWMutex m_pplinesMtx;
-};
-
-/// On disk pipeline cache.
-class PipelineCache : public MakeSingleton<PipelineCache>
-{
-public:
-	VkPipelineCache m_cacheHandle = VK_NULL_HANDLE;
-#if ANKI_PLATFORM_MOBILE
-	/// Workaround a bug in Qualcomm
-	Mutex* m_globalCreatePipelineMtx = nullptr;
-#endif
-
-	~PipelineCache()
-	{
-		destroy();
-	}
-
-	Error init(CString cacheDir);
-
-private:
-	GrString m_dumpFilename;
-	PtrSize m_dumpSize = 0;
-
-	void destroy();
-	Error destroyInternal();
-};
-/// @}
-
-} // end namespace anki

+ 2 - 8
AnKi/Gr/Vulkan/VkShaderProgram.cpp

@@ -6,7 +6,7 @@
 #include <AnKi/Gr/Vulkan/VkShaderProgram.h>
 #include <AnKi/Gr/Vulkan/VkShader.h>
 #include <AnKi/Gr/Vulkan/VkGrManager.h>
-#include <AnKi/Gr/Vulkan/VkPipelineFactory.h>
+#include <AnKi/Gr/Vulkan/VkGraphicsState.h>
 #include <AnKi/Gr/BackendCommon/Functions.h>
 #include <AnKi/Gr/Vulkan/VkBuffer.h>
 
@@ -140,7 +140,6 @@ ShaderProgramImpl::~ShaderProgramImpl()
 
 	if(m_graphics.m_pplineFactory)
 	{
-		m_graphics.m_pplineFactory->destroy();
 		deleteInstance(GrMemoryPool::getSingleton(), m_graphics.m_pplineFactory);
 	}
 
@@ -295,7 +294,7 @@ Error ShaderProgramImpl::init(const ShaderProgramInitInfo& inf)
 	//
 	if(graphicsProg)
 	{
-		m_graphics.m_pplineFactory = anki::newInstance<PipelineFactory>(GrMemoryPool::getSingleton());
+		m_graphics.m_pplineFactory = anki::newInstance<GraphicsPipelineFactory>(GrMemoryPool::getSingleton());
 	}
 
 	// Create the pipeline if compute
@@ -436,11 +435,6 @@ Error ShaderProgramImpl::init(const ShaderProgramInitInfo& inf)
 		const U32 size = s->getShaderBinarySize();
 
 		m_shaderBinarySizes[type] = size;
-
-		if(type == ShaderType::kFragment)
-		{
-			m_hasDiscard = s->hasDiscard();
-		}
 	}
 
 	// Non graphics programs have created their pipeline, destroy the shader modules

+ 3 - 10
AnKi/Gr/Vulkan/VkShaderProgram.h

@@ -12,7 +12,7 @@
 namespace anki {
 
 // Forward
-class PipelineFactory;
+class GraphicsPipelineFactory;
 
 /// @addtogroup vulkan
 /// @{
@@ -47,13 +47,8 @@ public:
 		return *m_pplineLayout;
 	}
 
-	const ShaderReflection& getReflectionInfo() const
-	{
-		return m_refl;
-	}
-
 	/// Only for graphics programs.
-	PipelineFactory& getPipelineFactory()
+	GraphicsPipelineFactory& getGraphicsPipelineFactory()
 	{
 		ANKI_ASSERT(m_graphics.m_pplineFactory);
 		return *m_graphics.m_pplineFactory;
@@ -99,14 +94,12 @@ private:
 
 	PipelineLayout2* m_pplineLayout = nullptr;
 
-	ShaderReflection m_refl;
-
 	class
 	{
 	public:
 		Array<VkPipelineShaderStageCreateInfo, U32(ShaderType::kFragment - ShaderType::kVertex) + 1> m_shaderCreateInfos = {};
 		U32 m_shaderCreateInfoCount = 0;
-		PipelineFactory* m_pplineFactory = nullptr;
+		GraphicsPipelineFactory* m_pplineFactory = nullptr;
 	} m_graphics;
 
 	class

+ 0 - 1
AnKi/Renderer/GBuffer.cpp

@@ -188,7 +188,6 @@ void GBuffer::populateRenderGraph(RenderingContext& ctx)
 
 		// Set some state, leave the rest to default
 		cmdb.setViewport(0, 0, getRenderer().getInternalResolution().x(), getRenderer().getInternalResolution().y());
-		cmdb.setRasterizationOrder(RasterizationOrder::kRelaxed);
 
 		const Bool enableVrs = GrManager::getSingleton().getDeviceCapabilities().m_vrs && g_vrsCVar.get() && g_gbufferVrsCVar.get();
 		if(enableVrs)

+ 2 - 2
AnKi/ShaderCompiler/ShaderCompiler.cpp

@@ -339,7 +339,7 @@ Error doReflectionSpirv(ConstWeakArray<U8> spirv, ShaderType type, ShaderReflect
 				return Error::kUserData;
 			}
 
-			refl.m_vertex.m_vertexAttributeLocations[a] = U8(location);
+			refl.m_vertex.m_vkVertexAttributeLocations[a] = U8(location);
 		}
 	}
 
@@ -615,7 +615,7 @@ Error doReflectionDxil(ConstWeakArray<U8> dxil, ShaderType type, ShaderReflectio
 #	undef ANKI_ATTRIB_NAME
 
 			refl.m_vertex.m_vertexAttributeMask.set(a);
-			refl.m_vertex.m_vertexAttributeLocations[a] = U8(i);
+			refl.m_vertex.m_vkVertexAttributeLocations[a] = U8(i);
 		}
 	}
 

+ 2 - 0
AnKi/Util/Array.h

@@ -63,6 +63,8 @@ public:
 		return operator[](typename std::underlying_type<TEnum>::type(n));
 	}
 
+	Bool operator==(const Array&) const = default;
+
 	Iterator getBegin()
 	{
 		return &m_data[0];