Przeglądaj źródła

GR: Add shader program and state changes in the command buffer

Panagiotis Christopoulos Charitos 9 lat temu
rodzic
commit
a1d94a1315

+ 87 - 1
src/anki/gr/CommandBuffer.h

@@ -141,12 +141,51 @@ public:
 	/// @name State manipulation
 	/// @name State manipulation
 	/// @{
 	/// @{
 
 
+	/// Bind vertex buffer.
+	void bindVertexBuffer(BufferPtr buff, U32 binding, PtrSize offset, PtrSize stride);
+
+	/// Bind transient vertex buffer.
+	void bindVertexBuffer(BufferPtr buff, U32 binding, const TransientMemoryToken& token);
+
+	/// Setup a vertex attribute.
+	void setVertexAttribute(U32 location, U32 buffBinding, const PixelFormat& fmt, PtrSize relativeOffset);
+
+	/// Bind index buffer.
+	void bindIndexBuffer(BufferPtr buff, PtrSize offset, IndexType type);
+
+	/// Bind transient index buffer.
+	void bindIndexBuffer(const TransientMemoryToken& token, IndexType type);
+
+	/// Enable primitive restart.
+	void enablePrimitiveRestart(Bool enable);
+
 	/// Set the viewport.
 	/// Set the viewport.
 	void setViewport(U16 minx, U16 miny, U16 maxx, U16 maxy);
 	void setViewport(U16 minx, U16 miny, U16 maxx, U16 maxy);
 
 
-	/// Set depth offset and units.
+	/// Enable scissor test.
+	void enableScissorTest(Bool enable);
+
+	/// Set the scissor rect.
+	void setScissorRect(U16 minx, U16 miny, U16 maxx, U16 maxy);
+
+	/// Set fill mode.
+	void setFillMode(FillMode mode);
+
+	/// Set cull mode.
+	void setCullMode(FaceSelectionMask mode);
+
+	/// Set depth offset and units. Set zeros to both to disable it.
 	void setPolygonOffset(F32 factor, F32 units);
 	void setPolygonOffset(F32 factor, F32 units);
 
 
+	/// Set stencil operations. To disable stencil test put StencilOperation::KEEP to all operations.
+	void setStencilOperations(FaceSelectionMask face,
+		StencilOperation stencilFail,
+		StencilOperation stencilPassDepthFail,
+		StencilOperation stencilPassDepthPass);
+
+	/// Set stencil compare function.
+	void setStencilCompareFunction(FaceSelectionMask face, CompareOperation comp);
+
 	/// Set the stencil compare mask.
 	/// Set the stencil compare mask.
 	void setStencilCompareMask(FaceSelectionMask face, U32 mask);
 	void setStencilCompareMask(FaceSelectionMask face, U32 mask);
 
 
@@ -156,6 +195,53 @@ public:
 	/// Set the stencil reference.
 	/// Set the stencil reference.
 	void setStencilReference(FaceSelectionMask face, U32 ref);
 	void setStencilReference(FaceSelectionMask face, U32 ref);
 
 
+	/// Enable/disable depth write.
+	void enableDepthWrite(Bool enable);
+
+	/// Set depth compare function.
+	void setDepthCompareFunction(CompareOperation op);
+
+	/// Enable/disable alpha to coverage.
+	void enableAlphaToCoverage(Bool enable);
+
+	/// Set color channel write mask.
+	void setColorChannelWriteMask(U32 attachment, ColorBit mask);
+
+	/// Set blend methods. To disable blending set src to BlendMethod::ONE and dst BlendMethod::ZERO.
+	void setBlendMethods(U32 attachment, BlendMethod src, BlendMethod dst);
+
+	/// Set the blend function.
+	void setBlendFunction(U32 attachment, BlendFunction func);
+
+	/// Bind texture.
+	void bindTexture(
+		U32 set, U32 binding, TexturePtr tex, DepthStencilAspectMask aspect = DepthStencilAspectMask::DEPTH);
+
+	/// Bind texture and sample.
+	void bindTextureAndSampler(U32 set,
+		U32 binding,
+		TexturePtr tex,
+		SamplerPtr sampler,
+		DepthStencilAspectMask aspect = DepthStencilAspectMask::DEPTH);
+
+	/// Bind uniform buffer.
+	void bindUniformBuffer(U32 set, U32 binding, BufferPtr buff, PtrSize offset);
+
+	/// Bind transient uniform buffer.
+	void bindUniformBuffer(U32 set, U32 binding, const TransientMemoryToken& token);
+
+	/// Bind storage buffer.
+	void bindStorageBuffer(U32 set, U32 binding, BufferPtr buff, PtrSize offset);
+
+	/// Bind transient storage buffer.
+	void bindStorageBuffer(U32 set, U32 binding, const TransientMemoryToken& token);
+
+	/// Bind load/store image.
+	void bindImage(U32 set, U32 binding, TexturePtr img, U32 level);
+
+	/// Bind a program.
+	void bindShaderProgram(ShaderProgramPtr prog);
+
 	/// Bind pipeline.
 	/// Bind pipeline.
 	void bindPipeline(PipelinePtr ppline);
 	void bindPipeline(PipelinePtr ppline);
 
 

+ 1 - 15
src/anki/gr/Common.h

@@ -47,6 +47,7 @@ ANKI_GR_CLASS(Pipeline)
 ANKI_GR_CLASS(Framebuffer)
 ANKI_GR_CLASS(Framebuffer)
 ANKI_GR_CLASS(OcclusionQuery)
 ANKI_GR_CLASS(OcclusionQuery)
 ANKI_GR_CLASS(ResourceGroup)
 ANKI_GR_CLASS(ResourceGroup)
+ANKI_GR_CLASS(ShaderProgram)
 
 
 #undef ANKI_GR_CLASS
 #undef ANKI_GR_CLASS
 
 
@@ -58,21 +59,6 @@ ANKI_GR_CLASS(ResourceGroup)
 	friend class GenericPoolAllocator;                                                                                 \
 	friend class GenericPoolAllocator;                                                                                 \
 	friend class GrObjectCache;
 	friend class GrObjectCache;
 
 
-/// Graphics object type.
-enum GrObjectType : U16
-{
-	BUFFER,
-	COMMAND_BUFFER,
-	FRAMEBUFFER,
-	OCCLUSION_QUERY,
-	PIPELINE,
-	RESOURCE_GROUP,
-	SAMPLER,
-	SHADER,
-	TEXTURE,
-	COUNT
-};
-
 /// Knowing the ventor allows some optimizations
 /// Knowing the ventor allows some optimizations
 enum class GpuVendor : U8
 enum class GpuVendor : U8
 {
 {

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

@@ -39,7 +39,8 @@ enum class FillMode : U8
 {
 {
 	POINTS,
 	POINTS,
 	WIREFRAME,
 	WIREFRAME,
-	SOLID
+	SOLID,
+	COUNT
 };
 };
 
 
 enum class FaceSelectionMask : U8
 enum class FaceSelectionMask : U8
@@ -59,7 +60,8 @@ enum class CompareOperation : U8
 	GREATER,
 	GREATER,
 	GREATER_EQUAL,
 	GREATER_EQUAL,
 	NOT_EQUAL,
 	NOT_EQUAL,
-	NEVER
+	NEVER,
+	COUNT
 };
 };
 
 
 enum class StencilOperation : U8
 enum class StencilOperation : U8
@@ -72,6 +74,7 @@ enum class StencilOperation : U8
 	INVERT,
 	INVERT,
 	INCREMENT_AND_WRAP,
 	INCREMENT_AND_WRAP,
 	DECREMENT_AND_WRAP,
 	DECREMENT_AND_WRAP,
+	COUNT
 };
 };
 
 
 enum class BlendMethod : U8
 enum class BlendMethod : U8
@@ -94,7 +97,8 @@ enum class BlendMethod : U8
 	SRC1_COLOR,
 	SRC1_COLOR,
 	ONE_MINUS_SRC1_COLOR,
 	ONE_MINUS_SRC1_COLOR,
 	SRC1_ALPHA,
 	SRC1_ALPHA,
-	ONE_MINUS_SRC1_ALPHA
+	ONE_MINUS_SRC1_ALPHA,
+	COUNT
 };
 };
 
 
 enum class BlendFunction : U8
 enum class BlendFunction : U8
@@ -103,7 +107,8 @@ enum class BlendFunction : U8
 	SUBTRACT,
 	SUBTRACT,
 	REVERSE_SUBTRACT,
 	REVERSE_SUBTRACT,
 	MIN,
 	MIN,
-	MAX
+	MAX,
+	COUNT
 };
 };
 
 
 enum class VertexStepRate : U8
 enum class VertexStepRate : U8
@@ -410,6 +415,13 @@ enum class DepthStencilAspectMask : U8
 	DEPTH_STENCIL = DEPTH | STENCIL
 	DEPTH_STENCIL = DEPTH | STENCIL
 };
 };
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(DepthStencilAspectMask, inline)
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(DepthStencilAspectMask, inline)
+
+/// Index buffer's index type.
+enum class IndexType : U8
+{
+	U16,
+	U32
+};
 /// @}
 /// @}
 
 
 } // end namespace anki
 } // end namespace anki

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

@@ -15,6 +15,22 @@ namespace anki
 /// @addtogroup graphics
 /// @addtogroup graphics
 /// @{
 /// @{
 
 
+/// Graphics object type.
+enum GrObjectType : U16
+{
+	BUFFER,
+	COMMAND_BUFFER,
+	FRAMEBUFFER,
+	OCCLUSION_QUERY,
+	PIPELINE,
+	RESOURCE_GROUP,
+	SAMPLER,
+	SHADER,
+	TEXTURE,
+	SHADER_PROGRAM,
+	COUNT
+};
+
 /// Base of all graphics objects.
 /// Base of all graphics objects.
 class GrObject : public NonCopyable
 class GrObject : public NonCopyable
 {
 {

+ 43 - 0
src/anki/gr/ShaderProgram.h

@@ -0,0 +1,43 @@
+// Copyright (C) 2009-2016, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <anki/gr/GrObject.h>
+
+namespace anki
+{
+
+/// @addtogroup graphics
+/// @{
+
+/// GPU program.
+class ShaderProgram : public GrObject
+{
+	ANKI_GR_OBJECT
+
+anki_internal:
+	UniquePtr<ShaderProgramImpl> m_impl;
+
+	static const GrObjectType CLASS_TYPE = GrObjectType::SHADER_PROGRAM;
+
+	/// Construct.
+	ShaderProgram(GrManager* manager, U64 hash, GrObjectCache* cache);
+
+	/// Destroy.
+	~ShaderProgram();
+
+	/// Create vertex+fragment.
+	void init(ShaderPtr vert, ShaderPtr frag);
+
+	/// Create compute.
+	void init(ShaderPtr comp);
+
+	/// Create with all.
+	void init(ShaderPtr vert, ShaderPtr tessc, ShaderPtr tesse, ShaderPtr geom, ShaderPtr frag);
+};
+/// @}
+
+} // end namespace anki

+ 7 - 0
src/anki/gr/gl/BufferImpl.h

@@ -46,6 +46,13 @@ public:
 		glBindBufferRange(target, binding, m_glName, offset, size);
 		glBindBufferRange(target, binding, m_glName, offset, size);
 	}
 	}
 
 
+	void bind(GLenum target, U32 binding, PtrSize offset) const
+	{
+		ANKI_ASSERT(isCreated());
+		ANKI_ASSERT(offset + size <= m_size);
+		glBindBufferRange(target, binding, m_glName, offset, m_size - offset);
+	}
+
 	void write(GLuint pbo, U32 pboOffset, U32 offset, U32 size) const
 	void write(GLuint pbo, U32 pboOffset, U32 offset, U32 size) const
 	{
 	{
 		ANKI_ASSERT(isCreated());
 		ANKI_ASSERT(isCreated());

+ 623 - 109
src/anki/gr/gl/CommandBuffer.cpp

@@ -23,6 +23,10 @@
 #include <anki/gr/gl/TextureImpl.h>
 #include <anki/gr/gl/TextureImpl.h>
 #include <anki/gr/Buffer.h>
 #include <anki/gr/Buffer.h>
 #include <anki/gr/gl/BufferImpl.h>
 #include <anki/gr/gl/BufferImpl.h>
+#include <anki/gr/Sampler.h>
+#include <anki/gr/gl/SamplerImpl.h>
+#include <anki/gr/ShaderProgram.h>
+#include <anki/gr/gl/ShaderProgramImpl.h>
 
 
 #include <anki/core/Trace.h>
 #include <anki/core/Trace.h>
 
 
@@ -79,6 +83,139 @@ void CommandBuffer::finish()
 	getManager().getImplementation().getRenderingThread().finishCommandBuffer(CommandBufferPtr(this));
 	getManager().getImplementation().getRenderingThread().finishCommandBuffer(CommandBufferPtr(this));
 }
 }
 
 
+void CommandBuffer::bindVertexBuffer(BufferPtr buff, U32 binding, PtrSize offset, PtrSize stride)
+{
+	class Cmd final : public GlCommand
+	{
+	public:
+		BufferPtr m_buff;
+		U32 m_binding;
+		PtrSize m_offset;
+		PtrSize m_stride;
+
+		Cmd(BufferPtr buff, U32 binding, PtrSize offset, PtrSize stride)
+			: m_buff(buff)
+			, m_binding(binding)
+			, m_offset(offset)
+			, m_stride(stride)
+		{
+		}
+
+		Error operator()(GlState& state)
+		{
+			glBindVertexBuffer(m_binding, m_buff->m_impl->getGlName(), m_offset, m_stride);
+			return ErrorCode::NONE;
+		}
+	};
+
+	m_impl->pushBackNewCommand<Cmd>(buff, binding, offset, stride);
+}
+
+void CommandBuffer::bindVertexBuffer(BufferPtr buff, U32 binding, const TransientMemoryToken& token)
+{
+	ANKI_ASSERT(!"TODO");
+}
+
+void CommandBuffer::setVertexAttribute(U32 location, U32 buffBinding, const PixelFormat& fmt, PtrSize relativeOffset)
+{
+	class Cmd final : public GlCommand
+	{
+	public:
+		U32 m_location;
+		U32 m_buffBinding;
+		U8 m_compSize;
+		GLenum m_fmt;
+		Bool8 m_normalized;
+		PtrSize m_relativeOffset;
+
+		Cmd(U32 location, U32 buffBinding, U8 compSize, GLenum fmt, Bool normalized, PtrSize relativeOffset)
+			: m_location(location)
+			, m_buffBinding(buffBinding)
+			, m_compSize(compSize)
+			, m_fmt(fmt)
+			, m_normalized(normalized)
+			, m_relativeOffset(relativeOffset)
+		{
+		}
+
+		Error operator()(GlState& state)
+		{
+			glVertexAttribFormat(m_location, m_compSize, m_fmt, m_normalized, m_relativeOffset);
+			glVertexAttribBinding(m_location, m_buffBinding);
+			return ErrorCode::NONE;
+		}
+	};
+
+	m_impl->m_state.setVertexAttribute(location, buffBinding, fmt, relativeOffset, [=]() {
+		U compCount;
+		GLenum type;
+		Bool normalized;
+
+		convertVertexFormat(fmt, compCount, type, normalized);
+
+		m_impl->pushBackNewCommand<Cmd>(location, buffBinding, compCount, type, normalized, relativeOffset);
+	});
+}
+
+void CommandBuffer::bindIndexBuffer(BufferPtr buff, PtrSize offset, IndexType type)
+{
+	class Cmd final : public GlCommand
+	{
+	public:
+		BufferPtr m_buff;
+
+		Cmd(BufferPtr buff)
+			: m_buff(buff)
+		{
+		}
+
+		Error operator()(GlState& state)
+		{
+			glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_buff->m_impl->getGlName());
+			return ErrorCode::NONE;
+		}
+	};
+
+	m_impl->pushBackNewCommand<Cmd>(buff);
+	m_impl->m_state.m_indexBuffOffset = offset;
+	m_impl->m_state.m_indexType = convertIndexType(type);
+}
+
+void CommandBuffer::bindIndexBuffer(const TransientMemoryToken& token, IndexType type)
+{
+	ANKI_ASSERT(!"TODO");
+}
+
+void CommandBuffer::enablePrimitiveRestart(Bool enable)
+{
+	class Cmd final : public GlCommand
+	{
+	public:
+		Bool8 m_enable;
+
+		Cmd(Bool enable)
+			: m_enable(enable)
+		{
+		}
+
+		Error operator()(GlState& state)
+		{
+			if(m_enable)
+			{
+				glEnable(GL_PRIMITIVE_RESTART);
+			}
+			else
+			{
+				glDisable(GL_PRIMITIVE_RESTART);
+			}
+
+			return ErrorCode::NONE;
+		}
+	};
+
+	m_impl->m_state.enablePrimitiveRestart(enable, [=]() { m_impl->pushBackNewCommand<Cmd>(enable); });
+}
+
 void CommandBuffer::setViewport(U16 minx, U16 miny, U16 maxx, U16 maxy)
 void CommandBuffer::setViewport(U16 minx, U16 miny, U16 maxx, U16 maxy)
 {
 {
 	class ViewportCommand final : public GlCommand
 	class ViewportCommand final : public GlCommand
@@ -109,18 +246,69 @@ void CommandBuffer::setViewport(U16 minx, U16 miny, U16 maxx, U16 maxy)
 #if ANKI_ASSERTS_ENABLED
 #if ANKI_ASSERTS_ENABLED
 	m_impl->m_dbg.m_viewport = true;
 	m_impl->m_dbg.m_viewport = true;
 #endif
 #endif
-	m_impl->pushBackNewCommand<ViewportCommand>(minx, miny, maxx, maxy);
+
+	m_impl->m_state.setViewport(
+		minx, miny, maxx, maxy, [=]() { m_impl->pushBackNewCommand<ViewportCommand>(minx, miny, maxx, maxy); });
+}
+
+void CommandBuffer::setScissorRect(U16 minx, U16 miny, U16 maxx, U16 maxy)
+{
+	ANKI_ASSERT(!"TODO");
+}
+
+void CommandBuffer::setFillMode(FillMode mode)
+{
+	class Cmd final : public GlCommand
+	{
+	public:
+		GLenum m_fillMode;
+
+		Cmd(GLenum fillMode)
+			: m_fillMode(fillMode)
+		{
+		}
+
+		Error operator()(GlState& state)
+		{
+			glPolygonMode(GL_FRONT_AND_BACK, m_fillMode);
+			return ErrorCode::NONE;
+		}
+	};
+
+	m_impl->m_state.setFillMode(mode, [=]() { m_impl->pushBackNewCommand<Cmd>(convertFillMode(mode)); });
+}
+
+void CommandBuffer::setCullMode(FaceSelectionMask mode)
+{
+	class Cmd final : public GlCommand
+	{
+	public:
+		GLenum m_mode;
+
+		Cmd(GLenum mode)
+			: m_mode(mode)
+		{
+		}
+
+		Error operator()(GlState& state)
+		{
+			glCullFace(m_mode);
+			return ErrorCode::NONE;
+		}
+	};
+
+	m_impl->m_state.setCullMode(mode, [=]() { m_impl->pushBackNewCommand<Cmd>(convertFaceMode(mode)); });
 }
 }
 
 
 void CommandBuffer::setPolygonOffset(F32 factor, F32 units)
 void CommandBuffer::setPolygonOffset(F32 factor, F32 units)
 {
 {
-	class SetPolygonOffsetCommand final : public GlCommand
+	class Cmd final : public GlCommand
 	{
 	{
 	public:
 	public:
 		F32 m_factor;
 		F32 m_factor;
 		F32 m_units;
 		F32 m_units;
 
 
-		SetPolygonOffsetCommand(F32 factor, F32 units)
+		Cmd(F32 factor, F32 units)
 			: m_factor(factor)
 			: m_factor(factor)
 			, m_units(units)
 			, m_units(units)
 		{
 		{
@@ -145,7 +333,438 @@ void CommandBuffer::setPolygonOffset(F32 factor, F32 units)
 #if ANKI_ASSERTS_ENABLED
 #if ANKI_ASSERTS_ENABLED
 	m_impl->m_dbg.m_polygonOffset = true;
 	m_impl->m_dbg.m_polygonOffset = true;
 #endif
 #endif
-	m_impl->pushBackNewCommand<SetPolygonOffsetCommand>(factor, units);
+
+	m_impl->m_state.setPolygonOffset(factor, units, [=]() { m_impl->pushBackNewCommand<Cmd>(factor, units); });
+}
+
+void CommandBuffer::setStencilOperations(FaceSelectionMask face,
+	StencilOperation stencilFail,
+	StencilOperation stencilPassDepthFail,
+	StencilOperation stencilPassDepthPass)
+{
+	class Cmd final : public GlCommand
+	{
+	public:
+		GLenum m_face;
+		GLenum m_stencilFail;
+		GLenum m_stencilPassDepthFail;
+		GLenum m_stencilPassDepthPass;
+
+		Cmd(GLenum face, GLenum stencilFail, GLenum stencilPassDepthFail, GLenum stencilPassDepthPass)
+			: m_face(face)
+			, m_stencilFail(stencilFail)
+			, m_stencilPassDepthFail(stencilPassDepthFail)
+			, m_stencilPassDepthPass(stencilPassDepthPass)
+		{
+		}
+
+		Error operator()(GlState& state)
+		{
+			glStencilOpSeparate(m_face, m_stencilFail, m_stencilPassDepthFail, m_stencilPassDepthPass);
+			return ErrorCode::NONE;
+		}
+	};
+
+	m_impl->m_state.setStencilOperations(face, stencilFail, stencilPassDepthFail, stencilPassDepthPass, [=]() {
+
+		m_impl->pushBackNewCommand<Cmd>(convertFaceMode(face),
+			convertStencilOperation(stencilFail),
+			convertStencilOperation(stencilPassDepthFail),
+			convertStencilOperation(stencilPassDepthPass));
+	});
+}
+
+void CommandBuffer::setStencilCompareFunction(FaceSelectionMask face, CompareOperation comp)
+{
+	m_impl->m_state.setStencilCompareFunction(face, comp);
+}
+
+void CommandBuffer::setStencilCompareMask(FaceSelectionMask face, U32 mask)
+{
+	m_impl->m_state.setStencilCompareMask(face, mask);
+}
+
+void CommandBuffer::setStencilWriteMask(FaceSelectionMask face, U32 mask)
+{
+	class Cmd final : public GlCommand
+	{
+	public:
+		GLenum m_face;
+		U32 m_mask;
+
+		Cmd(GLenum face, U32 mask)
+			: m_face(face)
+			, m_mask(mask)
+		{
+		}
+
+		Error operator()(GlState& state)
+		{
+			glStencilMaskSeparate(m_face, m_mask);
+			return ErrorCode::NONE;
+		}
+	};
+
+	m_impl->m_state.setStencilWriteMask(
+		face, mask, [=]() { m_impl->pushBackNewCommand<Cmd>(convertFaceMode(face), mask); });
+}
+
+void CommandBuffer::setStencilReference(FaceSelectionMask face, U32 ref)
+{
+	m_impl->m_state.setStencilReference(face, ref);
+}
+
+void CommandBuffer::enableDepthWrite(Bool enable)
+{
+	class Cmd final : public GlCommand
+	{
+	public:
+		Bool8 m_enable;
+
+		Cmd(Bool enable)
+			: m_enable(enable)
+		{
+		}
+
+		Error operator()(GlState& state)
+		{
+			glDepthMask(m_enable);
+			return ErrorCode::NONE;
+		}
+	};
+
+	m_impl->m_state.enableDepthWrite(enable, [=]() { m_impl->pushBackNewCommand<Cmd>(enable); });
+}
+
+void CommandBuffer::setDepthCompareFunction(CompareOperation op)
+{
+	class Cmd final : public GlCommand
+	{
+	public:
+		GLenum m_op;
+
+		Cmd(GLenum op)
+			: m_op(op)
+		{
+		}
+
+		Error operator()(GlState& state)
+		{
+			glDepthFunc(m_op);
+			return ErrorCode::NONE;
+		}
+	};
+
+	m_impl->m_state.setDepthCompareFunction(
+		op, [=]() { m_impl->pushBackNewCommand<Cmd>(convertCompareOperation(op)); });
+}
+
+void CommandBuffer::enableAlphaToCoverage(Bool enable)
+{
+	ANKI_ASSERT(!"TODO");
+}
+
+void CommandBuffer::setColorChannelWriteMask(U32 attachment, ColorBit mask)
+{
+	class Cmd final : public GlCommand
+	{
+	public:
+		U8 m_attachment;
+		ColorBit m_mask;
+
+		Cmd(U8 attachment, ColorBit mask)
+			: m_attachment(attachment)
+			, m_mask(mask)
+		{
+		}
+
+		Error operator()(GlState&)
+		{
+			glColorMaski(m_attachment,
+				!!(m_mask & ColorBit::RED),
+				!!(m_mask & ColorBit::GREEN),
+				!!(m_mask & ColorBit::BLUE),
+				!!(m_mask & ColorBit::ALPHA));
+
+			return ErrorCode::NONE;
+		}
+	};
+
+	m_impl->m_state.setColorChannelWriteMask(
+		attachment, mask, [=]() { m_impl->pushBackNewCommand<Cmd>(attachment, mask); });
+}
+
+void CommandBuffer::setBlendMethods(U32 attachment, BlendMethod src, BlendMethod dst)
+{
+	class Cmd final : public GlCommand
+	{
+	public:
+		U8 m_attachment;
+		GLenum m_src;
+		GLenum m_dst;
+
+		Cmd(U8 att, GLenum src, GLenum dst)
+			: m_attachment(att)
+			, m_src(src)
+			, m_dst(dst)
+		{
+		}
+
+		Error operator()(GlState&)
+		{
+			glBlendFunci(m_attachment, m_src, m_dst);
+			return ErrorCode::NONE;
+		}
+	};
+
+	m_impl->m_state.setBlendMethods(attachment, src, dst, [=]() {
+		m_impl->pushBackNewCommand<Cmd>(attachment, convertBlendMethod(src), convertBlendMethod(dst));
+	});
+}
+
+void CommandBuffer::setBlendFunction(U32 attachment, BlendFunction func)
+{
+	class Cmd final : public GlCommand
+	{
+	public:
+		U8 m_attachment;
+		GLenum m_func;
+
+		Cmd(U8 att, GLenum func)
+			: m_attachment(att)
+			, m_func(func)
+		{
+		}
+
+		Error operator()(GlState&)
+		{
+			glBlendEquationi(m_attachment, m_func);
+			return ErrorCode::NONE;
+		}
+	};
+
+	m_impl->m_state.setBlendFunction(
+		attachment, func, [=]() { m_impl->pushBackNewCommand<Cmd>(attachment, convertBlendFunction(func)); });
+}
+
+void CommandBuffer::bindTexture(U32 set, U32 binding, TexturePtr tex, DepthStencilAspectMask aspect)
+{
+	class Cmd final : public GlCommand
+	{
+	public:
+		U32 m_unit;
+		TexturePtr m_tex;
+
+		Cmd(U32 unit, TexturePtr tex)
+			: m_unit(unit)
+			, m_tex(tex)
+		{
+		}
+
+		Error operator()(GlState&)
+		{
+			glBindTextureUnit(m_unit, m_tex->m_impl->getGlName());
+			glBindSampler(m_unit, 0);
+			return ErrorCode::NONE;
+		}
+	};
+
+	U unit = binding + MAX_TEXTURE_BINDINGS * set;
+	m_impl->pushBackNewCommand<Cmd>(unit, tex);
+}
+
+void CommandBuffer::bindTextureAndSampler(
+	U32 set, U32 binding, TexturePtr tex, SamplerPtr sampler, DepthStencilAspectMask aspect)
+{
+	class Cmd final : public GlCommand
+	{
+	public:
+		U32 m_unit;
+		TexturePtr m_tex;
+		SamplerPtr m_sampler;
+
+		Cmd(U32 unit, TexturePtr tex, SamplerPtr sampler)
+			: m_unit(unit)
+			, m_tex(tex)
+			, m_sampler(sampler)
+		{
+		}
+
+		Error operator()(GlState&)
+		{
+			glBindTextureUnit(m_unit, m_tex->m_impl->getGlName());
+			glBindSampler(m_unit, m_sampler->m_impl->getGlName());
+			return ErrorCode::NONE;
+		}
+	};
+
+	U unit = binding + MAX_TEXTURE_BINDINGS * set;
+	m_impl->pushBackNewCommand<Cmd>(unit, tex, sampler);
+}
+
+void CommandBuffer::bindUniformBuffer(U32 set, U32 binding, BufferPtr buff, PtrSize offset)
+{
+	class Cmd final : public GlCommand
+	{
+	public:
+		U32 m_binding;
+		BufferPtr m_buff;
+		PtrSize m_offset;
+
+		Cmd(U32 binding, BufferPtr buff, PtrSize offset)
+			: m_binding(binding)
+			, m_buff(buff)
+			, m_offset(offset)
+		{
+		}
+
+		Error operator()(GlState&)
+		{
+			m_buff->m_impl->bind(GL_UNIFORM_BUFFER, m_binding, m_offset);
+			return ErrorCode::NONE;
+		}
+	};
+
+	binding = binding + MAX_UNIFORM_BUFFER_BINDINGS * set;
+	m_impl->pushBackNewCommand<Cmd>(binding, buff, offset);
+}
+
+void CommandBuffer::bindUniformBuffer(U32 set, U32 binding, const TransientMemoryToken& token)
+{
+	class Cmd final : public GlCommand
+	{
+	public:
+		U32 m_binding;
+		TransientMemoryToken m_token;
+		GLuint m_name;
+
+		Cmd(U32 binding, const TransientMemoryToken& token, GLuint name)
+			: m_binding(binding)
+			, m_token(token)
+			, m_name(name)
+		{
+		}
+
+		Error operator()(GlState& state)
+		{
+			glBindBufferRange(GL_UNIFORM_BUFFER, m_binding, m_name, m_token.m_offset, m_token.m_range);
+			return ErrorCode::NONE;
+		}
+	};
+
+	binding = binding + MAX_UNIFORM_BUFFER_BINDINGS * set;
+	GLuint name = getManager().getImplementation().getTransientMemoryManager().getGlName(token);
+	m_impl->pushBackNewCommand<Cmd>(binding, token, name);
+}
+
+void CommandBuffer::bindStorageBuffer(U32 set, U32 binding, BufferPtr buff, PtrSize offset)
+{
+	class Cmd final : public GlCommand
+	{
+	public:
+		U32 m_binding;
+		BufferPtr m_buff;
+		PtrSize m_offset;
+
+		Cmd(U32 binding, BufferPtr buff, PtrSize offset)
+			: m_binding(binding)
+			, m_buff(buff)
+			, m_offset(offset)
+		{
+		}
+
+		Error operator()(GlState&)
+		{
+			m_buff->m_impl->bind(GL_SHADER_STORAGE_BUFFER, m_binding, m_offset);
+			return ErrorCode::NONE;
+		}
+	};
+
+	binding = binding + MAX_STORAGE_BUFFER_BINDINGS * set;
+	m_impl->pushBackNewCommand<Cmd>(binding, buff, offset);
+}
+
+void CommandBuffer::bindStorageBuffer(U32 set, U32 binding, const TransientMemoryToken& token)
+{
+	class Cmd final : public GlCommand
+	{
+	public:
+		U32 m_binding;
+		TransientMemoryToken m_token;
+		GLuint m_name;
+
+		Cmd(U32 binding, const TransientMemoryToken& token, GLuint name)
+			: m_binding(binding)
+			, m_token(token)
+			, m_name(name)
+		{
+		}
+
+		Error operator()(GlState& state)
+		{
+			glBindBufferRange(GL_SHADER_STORAGE_BUFFER, m_binding, m_name, m_token.m_offset, m_token.m_range);
+			return ErrorCode::NONE;
+		}
+	};
+
+	binding = binding + MAX_STORAGE_BUFFER_BINDINGS * set;
+	GLuint name = getManager().getImplementation().getTransientMemoryManager().getGlName(token);
+	m_impl->pushBackNewCommand<Cmd>(binding, token, name);
+}
+
+void CommandBuffer::bindImage(U32 set, U32 binding, TexturePtr img, U32 level)
+{
+	class Cmd final : public GlCommand
+	{
+	public:
+		TexturePtr m_img;
+		U16 m_unit;
+		U8 m_level;
+
+		Cmd(U32 unit, TexturePtr img, U32 level)
+			: m_img(img)
+			, m_unit(unit)
+			, m_level(level)
+		{
+		}
+
+		Error operator()(GlState&)
+		{
+			glBindImageTexture(m_unit,
+				m_img->m_impl->getGlName(),
+				m_level,
+				GL_TRUE,
+				0,
+				GL_READ_WRITE,
+				m_img->m_impl->m_internalFormat);
+			return ErrorCode::NONE;
+		}
+	};
+
+	binding = binding + set * MAX_IMAGE_BINDINGS;
+	m_impl->pushBackNewCommand<Cmd>(binding, img, level);
+}
+
+void CommandBuffer::bindShaderProgram(ShaderProgramPtr prog)
+{
+	class Cmd final : public GlCommand
+	{
+	public:
+		ShaderProgramPtr m_prog;
+
+		Cmd(const ShaderProgramPtr& prog)
+			: m_prog(prog)
+		{
+		}
+
+		Error operator()(GlState&)
+		{
+			glUseProgram(m_prog->m_impl->getGlName());
+			return ErrorCode::NONE;
+		}
+	};
+
+	m_impl->m_state.bindShaderProgram(prog, [=]() { m_impl->pushBackNewCommand<Cmd>(prog); });
 }
 }
 
 
 void CommandBuffer::bindPipeline(PipelinePtr ppline)
 void CommandBuffer::bindPipeline(PipelinePtr ppline)
@@ -674,109 +1293,4 @@ void CommandBuffer::writeOcclusionQueryResultToBuffer(OcclusionQueryPtr query, P
 	m_impl->pushBackNewCommand<WriteOcclResultToBuff>(query, offset, buff);
 	m_impl->pushBackNewCommand<WriteOcclResultToBuff>(query, offset, buff);
 }
 }
 
 
-void CommandBuffer::setStencilCompareMask(FaceSelectionMask face, U32 mask)
-{
-	class SetStencilCompareMask final : public GlCommand
-	{
-	public:
-		FaceSelectionMask m_face;
-		U32 m_mask;
-
-		SetStencilCompareMask(FaceSelectionMask face, U32 mask)
-			: m_face(face)
-			, m_mask(mask)
-		{
-		}
-
-		Error operator()(GlState& state)
-		{
-			if(!!(m_face & FaceSelectionMask::FRONT) && state.m_stencilCompareMask[0] != m_mask)
-			{
-				state.m_stencilCompareMask[0] = m_mask;
-				state.m_glStencilFuncSeparateDirtyMask |= 1 << 0;
-			}
-
-			if(!!(m_face & FaceSelectionMask::BACK) && state.m_stencilCompareMask[1] != m_mask)
-			{
-				state.m_stencilCompareMask[1] = m_mask;
-				state.m_glStencilFuncSeparateDirtyMask |= 1 << 1;
-			}
-
-			return ErrorCode::NONE;
-		}
-	};
-
-	m_impl->pushBackNewCommand<SetStencilCompareMask>(face, mask);
-}
-
-void CommandBuffer::setStencilWriteMask(FaceSelectionMask face, U32 mask)
-{
-	class SetStencilWriteMask final : public GlCommand
-	{
-	public:
-		FaceSelectionMask m_face;
-		U32 m_mask;
-
-		SetStencilWriteMask(FaceSelectionMask face, U32 mask)
-			: m_face(face)
-			, m_mask(mask)
-		{
-		}
-
-		Error operator()(GlState& state)
-		{
-			if(!!(m_face & FaceSelectionMask::FRONT) && state.m_stencilWriteMask[0] != m_mask)
-			{
-				glStencilMaskSeparate(GL_FRONT, m_mask);
-				state.m_stencilWriteMask[0] = m_mask;
-			}
-
-			if(!!(m_face & FaceSelectionMask::BACK) && state.m_stencilWriteMask[1] != m_mask)
-			{
-				glStencilMaskSeparate(GL_BACK, m_mask);
-				state.m_stencilWriteMask[1] = m_mask;
-			}
-
-			return ErrorCode::NONE;
-		}
-	};
-
-	m_impl->pushBackNewCommand<SetStencilWriteMask>(face, mask);
-}
-
-void CommandBuffer::setStencilReference(FaceSelectionMask face, U32 ref)
-{
-	class SetStencilRefMask final : public GlCommand
-	{
-	public:
-		FaceSelectionMask m_face;
-		U32 m_ref;
-
-		SetStencilRefMask(FaceSelectionMask face, U32 ref)
-			: m_face(face)
-			, m_ref(ref)
-		{
-		}
-
-		Error operator()(GlState& state)
-		{
-			if(!!(m_face & FaceSelectionMask::FRONT) && state.m_stencilRef[0] != m_ref)
-			{
-				state.m_stencilRef[0] = m_ref;
-				state.m_glStencilFuncSeparateDirtyMask |= 1 << 0;
-			}
-
-			if(!!(m_face & FaceSelectionMask::BACK) && state.m_stencilRef[1] != m_ref)
-			{
-				state.m_stencilRef[1] = m_ref;
-				state.m_glStencilFuncSeparateDirtyMask |= 1 << 1;
-			}
-
-			return ErrorCode::NONE;
-		}
-	};
-
-	m_impl->pushBackNewCommand<SetStencilRefMask>(face, ref);
-}
-
 } // end namespace anki
 } // end namespace anki

+ 14 - 11
src/anki/gr/gl/CommandBufferImpl.h

@@ -6,6 +6,7 @@
 #pragma once
 #pragma once
 
 
 #include <anki/gr/CommandBuffer.h>
 #include <anki/gr/CommandBuffer.h>
+#include <anki/gr/gl/StateTracker.h>
 #include <anki/util/Assert.h>
 #include <anki/util/Assert.h>
 #include <anki/util/Allocator.h>
 #include <anki/util/Allocator.h>
 
 
@@ -52,6 +53,19 @@ public:
 	} m_dbg;
 	} m_dbg;
 #endif
 #endif
 
 
+	GrManager* m_manager = nullptr;
+	GlCommand* m_firstCommand = nullptr;
+	GlCommand* m_lastCommand = nullptr;
+	CommandBufferAllocator<U8> m_alloc;
+	Bool8 m_immutable = false;
+	CommandBufferFlag m_flags;
+
+#if ANKI_DEBUG
+	Bool8 m_executed = false;
+#endif
+
+	StateTracker m_state;
+
 	/// Default constructor
 	/// Default constructor
 	CommandBufferImpl(GrManager* manager)
 	CommandBufferImpl(GrManager* manager)
 		: m_manager(manager)
 		: m_manager(manager)
@@ -137,17 +151,6 @@ public:
 	}
 	}
 
 
 private:
 private:
-	GrManager* m_manager = nullptr;
-	GlCommand* m_firstCommand = nullptr;
-	GlCommand* m_lastCommand = nullptr;
-	CommandBufferAllocator<U8> m_alloc;
-	Bool8 m_immutable = false;
-	CommandBufferFlag m_flags;
-
-#if ANKI_DEBUG
-	Bool8 m_executed = false;
-#endif
-
 	void destroy();
 	void destroy();
 
 
 	void checkDrawcall() const
 	void checkDrawcall() const

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

@@ -126,4 +126,137 @@ void convertFilter(SamplingFilter minMagFilter, SamplingFilter mipFilter, GLenum
 	}
 	}
 }
 }
 
 
+void convertVertexFormat(const PixelFormat& fmt, U& compCount, GLenum& type, Bool& normalized)
+{
+	if(fmt == PixelFormat(ComponentFormat::R32, TransformFormat::FLOAT))
+	{
+		compCount = 1;
+		type = GL_FLOAT;
+		normalized = false;
+	}
+	else if(fmt == PixelFormat(ComponentFormat::R32G32, TransformFormat::FLOAT))
+	{
+		compCount = 2;
+		type = GL_FLOAT;
+		normalized = false;
+	}
+	else if(fmt == PixelFormat(ComponentFormat::R32G32B32, TransformFormat::FLOAT))
+	{
+		compCount = 3;
+		type = GL_FLOAT;
+		normalized = false;
+	}
+	else if(fmt == PixelFormat(ComponentFormat::R32G32B32A32, TransformFormat::FLOAT))
+	{
+		compCount = 4;
+		type = GL_FLOAT;
+		normalized = false;
+	}
+	else if(fmt == PixelFormat(ComponentFormat::R16G16, TransformFormat::FLOAT))
+	{
+		compCount = 2;
+		type = GL_HALF_FLOAT;
+		normalized = false;
+	}
+	else if(fmt == PixelFormat(ComponentFormat::R16G16, TransformFormat::UNORM))
+	{
+		compCount = 2;
+		type = GL_UNSIGNED_SHORT;
+		normalized = true;
+	}
+	else if(fmt == PixelFormat(ComponentFormat::R10G10B10A2, TransformFormat::SNORM))
+	{
+		compCount = 4;
+		type = GL_INT_2_10_10_10_REV;
+		normalized = true;
+	}
+	else if(fmt == PixelFormat(ComponentFormat::R8G8B8A8, TransformFormat::UNORM))
+	{
+		compCount = 4;
+		type = GL_UNSIGNED_BYTE;
+		normalized = true;
+	}
+	else if(fmt == PixelFormat(ComponentFormat::R8G8B8, TransformFormat::UNORM))
+	{
+		compCount = 3;
+		type = GL_UNSIGNED_BYTE;
+		normalized = true;
+	}
+	else
+	{
+		ANKI_ASSERT(0 && "TODO");
+	}
+}
+
+GLenum convertBlendMethod(BlendMethod in)
+{
+	GLenum out;
+
+	switch(in)
+	{
+	case BlendMethod::ZERO:
+		out = GL_ZERO;
+		break;
+	case BlendMethod::ONE:
+		out = GL_ONE;
+		break;
+	case BlendMethod::SRC_COLOR:
+		out = GL_SRC_COLOR;
+		break;
+	case BlendMethod::ONE_MINUS_SRC_COLOR:
+		out = GL_ONE_MINUS_SRC_COLOR;
+		break;
+	case BlendMethod::DST_COLOR:
+		out = GL_DST_COLOR;
+		break;
+	case BlendMethod::ONE_MINUS_DST_COLOR:
+		out = GL_ONE_MINUS_DST_COLOR;
+		break;
+	case BlendMethod::SRC_ALPHA:
+		out = GL_SRC_ALPHA;
+		break;
+	case BlendMethod::ONE_MINUS_SRC_ALPHA:
+		out = GL_ONE_MINUS_SRC_ALPHA;
+		break;
+	case BlendMethod::DST_ALPHA:
+		out = GL_DST_ALPHA;
+		break;
+	case BlendMethod::ONE_MINUS_DST_ALPHA:
+		out = GL_ONE_MINUS_DST_ALPHA;
+		break;
+	case BlendMethod::CONSTANT_COLOR:
+		out = GL_CONSTANT_COLOR;
+		break;
+	case BlendMethod::ONE_MINUS_CONSTANT_COLOR:
+		out = GL_ONE_MINUS_CONSTANT_COLOR;
+		break;
+	case BlendMethod::CONSTANT_ALPHA:
+		out = GL_CONSTANT_ALPHA;
+		break;
+	case BlendMethod::ONE_MINUS_CONSTANT_ALPHA:
+		out = GL_ONE_MINUS_CONSTANT_ALPHA;
+		break;
+	case BlendMethod::SRC_ALPHA_SATURATE:
+		out = GL_SRC_ALPHA_SATURATE;
+		break;
+	case BlendMethod::SRC1_COLOR:
+		out = GL_SRC1_COLOR;
+		break;
+	case BlendMethod::ONE_MINUS_SRC1_COLOR:
+		out = GL_ONE_MINUS_SRC1_COLOR;
+		break;
+	case BlendMethod::SRC1_ALPHA:
+		out = GL_SRC1_ALPHA;
+		break;
+	case BlendMethod::ONE_MINUS_SRC1_ALPHA:
+		out = GL_ONE_MINUS_SRC1_ALPHA;
+		break;
+	default:
+		ANKI_ASSERT(0);
+		out = 0;
+	}
+
+	return out;
+}
+
 } // end namespace anki
 } // end namespace anki

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

@@ -57,6 +57,80 @@ inline GLenum convertFaceMode(FaceSelectionMask in)
 }
 }
 
 
 void convertFilter(SamplingFilter minMagFilter, SamplingFilter mipFilter, GLenum& minFilter, GLenum& magFilter);
 void convertFilter(SamplingFilter minMagFilter, SamplingFilter mipFilter, GLenum& minFilter, GLenum& magFilter);
+
+void convertVertexFormat(const PixelFormat& fmt, U& compCount, GLenum& type, Bool& normalized);
+
+inline GLenum convertIndexType(IndexType ak)
+{
+	GLenum out;
+	switch(ak)
+	{
+	case IndexType::U16:
+		out = GL_UNSIGNED_SHORT;
+		break;
+	case IndexType::U32:
+		out = GL_UNSIGNED_INT;
+		break;
+	default:
+		ANKI_ASSERT(0);
+		out = 0;
+	}
+
+	return out;
+}
+
+inline GLenum convertFillMode(FillMode mode)
+{
+	GLenum out;
+	switch(mode)
+	{
+	case FillMode::POINTS:
+		out = GL_POINT;
+		break;
+	case FillMode::WIREFRAME:
+		out = GL_LINE;
+		break;
+	case FillMode::SOLID:
+		out = GL_FILL;
+		break;
+	default:
+		ANKI_ASSERT(0);
+		out = 0;
+	}
+
+	return out;
+}
+
+GLenum convertBlendMethod(BlendMethod in);
+
+inline GLenum convertBlendFunction(BlendFunction ak)
+{
+	GLenum out;
+
+	switch(ak)
+	{
+	case BlendFunction::ADD:
+		out = GL_FUNC_ADD;
+		break;
+	case BlendFunction::SUBTRACT:
+		out = GL_FUNC_SUBTRACT;
+		break;
+	case BlendFunction::REVERSE_SUBTRACT:
+		out = GL_FUNC_REVERSE_SUBTRACT;
+		break;
+	case BlendFunction::MIN:
+		out = GL_MIN;
+		break;
+	case BlendFunction::MAX:
+		out = GL_MAX;
+		break;
+	default:
+		ANKI_ASSERT(0);
+		out = 0;
+	}
+
+	return out;
+}
 /// @}
 /// @}
 
 
 } // end namespace anki
 } // end namespace anki

+ 0 - 70
src/anki/gr/gl/PipelineImpl.cpp

@@ -13,76 +13,6 @@
 namespace anki
 namespace anki
 {
 {
 
 
-static GLenum convertBlendMethod(BlendMethod in)
-{
-	GLenum out = 0;
-
-	switch(in)
-	{
-	case BlendMethod::ZERO:
-		out = GL_ZERO;
-		break;
-	case BlendMethod::ONE:
-		out = GL_ONE;
-		break;
-	case BlendMethod::SRC_COLOR:
-		out = GL_SRC_COLOR;
-		break;
-	case BlendMethod::ONE_MINUS_SRC_COLOR:
-		out = GL_ONE_MINUS_SRC_COLOR;
-		break;
-	case BlendMethod::DST_COLOR:
-		out = GL_DST_COLOR;
-		break;
-	case BlendMethod::ONE_MINUS_DST_COLOR:
-		out = GL_ONE_MINUS_DST_COLOR;
-		break;
-	case BlendMethod::SRC_ALPHA:
-		out = GL_SRC_ALPHA;
-		break;
-	case BlendMethod::ONE_MINUS_SRC_ALPHA:
-		out = GL_ONE_MINUS_SRC_ALPHA;
-		break;
-	case BlendMethod::DST_ALPHA:
-		out = GL_DST_ALPHA;
-		break;
-	case BlendMethod::ONE_MINUS_DST_ALPHA:
-		out = GL_ONE_MINUS_DST_ALPHA;
-		break;
-	case BlendMethod::CONSTANT_COLOR:
-		out = GL_CONSTANT_COLOR;
-		break;
-	case BlendMethod::ONE_MINUS_CONSTANT_COLOR:
-		out = GL_ONE_MINUS_CONSTANT_COLOR;
-		break;
-	case BlendMethod::CONSTANT_ALPHA:
-		out = GL_CONSTANT_ALPHA;
-		break;
-	case BlendMethod::ONE_MINUS_CONSTANT_ALPHA:
-		out = GL_ONE_MINUS_CONSTANT_ALPHA;
-		break;
-	case BlendMethod::SRC_ALPHA_SATURATE:
-		out = GL_SRC_ALPHA_SATURATE;
-		break;
-	case BlendMethod::SRC1_COLOR:
-		out = GL_SRC1_COLOR;
-		break;
-	case BlendMethod::ONE_MINUS_SRC1_COLOR:
-		out = GL_ONE_MINUS_SRC1_COLOR;
-		break;
-	case BlendMethod::SRC1_ALPHA:
-		out = GL_SRC1_ALPHA;
-		break;
-	case BlendMethod::ONE_MINUS_SRC1_ALPHA:
-		out = GL_ONE_MINUS_SRC1_ALPHA;
-		break;
-	default:
-		ANKI_ASSERT(0);
-	}
-
-	return out;
-}
-
 static void deletePrograms(GLsizei n, const GLuint* names)
 static void deletePrograms(GLsizei n, const GLuint* names)
 {
 {
 	ANKI_ASSERT(n == 1);
 	ANKI_ASSERT(n == 1);

+ 21 - 21
src/anki/gr/gl/Sampler.cpp

@@ -20,35 +20,35 @@ Sampler::~Sampler()
 {
 {
 }
 }
 
 
-class CreateSamplerCommand : public GlCommand
+void Sampler::init(const SamplerInitInfo& init)
 {
 {
-public:
-	SamplerPtr m_sampler;
-	SamplerInitInfo m_init;
-
-	CreateSamplerCommand(Sampler* sampler, const SamplerInitInfo& init)
-		: m_sampler(sampler)
-		, m_init(init)
+	class CreateSamplerCommand : public GlCommand
 	{
 	{
-	}
+	public:
+		SamplerPtr m_sampler;
+		SamplerInitInfo m_init;
 
 
-	Error operator()(GlState&)
-	{
-		SamplerImpl& impl = *m_sampler->m_impl;
+		CreateSamplerCommand(Sampler* sampler, const SamplerInitInfo& init)
+			: m_sampler(sampler)
+			, m_init(init)
+		{
+		}
 
 
-		impl.init(m_init);
+		Error operator()(GlState&)
+		{
+			SamplerImpl& impl = *m_sampler->m_impl;
 
 
-		GlObject::State oldState = impl.setStateAtomically(GlObject::State::CREATED);
+			impl.init(m_init);
 
 
-		(void)oldState;
-		ANKI_ASSERT(oldState == GlObject::State::TO_BE_CREATED);
+			GlObject::State oldState = impl.setStateAtomically(GlObject::State::CREATED);
 
 
-		return ErrorCode::NONE;
-	}
-};
+			(void)oldState;
+			ANKI_ASSERT(oldState == GlObject::State::TO_BE_CREATED);
+
+			return ErrorCode::NONE;
+		}
+	};
 
 
-void Sampler::init(const SamplerInitInfo& init)
-{
 	m_impl.reset(getAllocator().newInstance<SamplerImpl>(&getManager()));
 	m_impl.reset(getAllocator().newInstance<SamplerImpl>(&getManager()));
 
 
 	CommandBufferPtr cmdb = getManager().newInstance<CommandBuffer>(CommandBufferInitInfo());
 	CommandBufferPtr cmdb = getManager().newInstance<CommandBuffer>(CommandBufferInitInfo());

+ 117 - 0
src/anki/gr/gl/ShaderProgram.cpp

@@ -0,0 +1,117 @@
+// Copyright (C) 2009-2016, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/gr/ShaderProgram.h>
+#include <anki/gr/gl/ShaderProgramImpl.h>
+#include <anki/gr/gl/CommandBufferImpl.h>
+#include <anki/gr/Shader.h>
+
+namespace anki
+{
+
+ShaderProgram::ShaderProgram(GrManager* manager, U64 hash, GrObjectCache* cache)
+	: GrObject(manager, CLASS_TYPE, hash, cache)
+{
+}
+
+ShaderProgram::~ShaderProgram()
+{
+}
+
+void ShaderProgram::init(ShaderPtr vert, ShaderPtr frag)
+{
+	class CreateCommand final : public GlCommand
+	{
+	public:
+		ShaderProgramPtr m_prog;
+		ShaderPtr m_vert;
+		ShaderPtr m_frag;
+
+		CreateCommand(ShaderProgram* prog, ShaderPtr vert, ShaderPtr frag)
+			: m_prog(prog)
+			, m_vert(vert)
+			, m_frag(frag)
+		{
+		}
+
+		Error operator()(GlState&)
+		{
+			ShaderPtr none;
+			return m_prog->m_impl->initGraphics(m_vert, none, none, none, m_frag);
+		}
+	};
+
+	m_impl.reset(getAllocator().newInstance<ShaderProgramImpl>(&getManager()));
+
+	CommandBufferPtr cmdb = getManager().newInstance<CommandBuffer>(CommandBufferInitInfo());
+	cmdb->m_impl->pushBackNewCommand<CreateCommand>(this, vert, frag);
+	cmdb->flush();
+}
+
+void ShaderProgram::init(ShaderPtr comp)
+{
+	class CreateCommand final : public GlCommand
+	{
+	public:
+		ShaderProgramPtr m_prog;
+		ShaderPtr m_comp;
+
+		CreateCommand(ShaderProgram* prog, ShaderPtr comp)
+			: m_prog(prog)
+			, m_comp(comp)
+		{
+		}
+
+		Error operator()(GlState&)
+		{
+			ShaderPtr none;
+			return m_prog->m_impl->initCompute(m_comp);
+		}
+	};
+
+	m_impl.reset(getAllocator().newInstance<ShaderProgramImpl>(&getManager()));
+
+	CommandBufferPtr cmdb = getManager().newInstance<CommandBuffer>(CommandBufferInitInfo());
+	cmdb->m_impl->pushBackNewCommand<CreateCommand>(this, comp);
+	cmdb->flush();
+}
+
+void ShaderProgram::init(ShaderPtr vert, ShaderPtr tessc, ShaderPtr tesse, ShaderPtr geom, ShaderPtr frag)
+{
+	class CreateCommand final : public GlCommand
+	{
+	public:
+		ShaderProgramPtr m_prog;
+		ShaderPtr m_vert;
+		ShaderPtr m_tessc;
+		ShaderPtr m_tesse;
+		ShaderPtr m_geom;
+		ShaderPtr m_frag;
+
+		CreateCommand(
+			ShaderProgram* prog, ShaderPtr vert, ShaderPtr tessc, ShaderPtr tesse, ShaderPtr geom, ShaderPtr frag)
+			: m_prog(prog)
+			, m_vert(vert)
+			, m_tessc(tesse)
+			, m_tesse(tesse)
+			, m_geom(geom)
+			, m_frag(frag)
+		{
+		}
+
+		Error operator()(GlState&)
+		{
+			return m_prog->m_impl->initGraphics(m_vert, m_tessc, m_tesse, m_geom, m_frag);
+		}
+	};
+
+	m_impl.reset(getAllocator().newInstance<ShaderProgramImpl>(&getManager()));
+
+	CommandBufferPtr cmdb = getManager().newInstance<CommandBuffer>(CommandBufferInitInfo());
+	cmdb->m_impl->pushBackNewCommand<CreateCommand>(this, vert, tessc, tesse, geom, frag);
+	cmdb->flush();
+}
+
+} // end namespace anki

+ 76 - 0
src/anki/gr/gl/ShaderProgramImpl.cpp

@@ -0,0 +1,76 @@
+// Copyright (C) 2009-2016, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/gr/gl/ShaderProgramImpl.h>
+#include <anki/gr/Shader.h>
+#include <anki/gr/gl/ShaderImpl.h>
+
+namespace anki
+{
+
+Error ShaderProgramImpl::initGraphics(ShaderPtr vert, ShaderPtr tessc, ShaderPtr tesse, ShaderPtr geom, ShaderPtr frag)
+{
+	m_glName = glCreateProgram();
+	ANKI_ASSERT(m_glName != 0);
+
+	glAttachShader(m_glName, vert->m_impl->getGlName());
+
+	if(tessc)
+	{
+		glAttachShader(m_glName, tessc->m_impl->getGlName());
+	}
+
+	if(tesse)
+	{
+		glAttachShader(m_glName, tesse->m_impl->getGlName());
+	}
+
+	if(geom)
+	{
+		glAttachShader(m_glName, geom->m_impl->getGlName());
+	}
+
+	glAttachShader(m_glName, frag->m_impl->getGlName());
+
+	return link(vert->m_impl->getGlName(), frag->m_impl->getGlName());
+}
+
+Error ShaderProgramImpl::initCompute(ShaderPtr comp)
+{
+	m_glName = glCreateProgram();
+	ANKI_ASSERT(m_glName != 0);
+
+	return link(0, 0);
+}
+
+Error ShaderProgramImpl::link(GLuint vert, GLuint frag)
+{
+	Error err = ErrorCode::NONE;
+
+	glLinkProgram(m_glName);
+	GLint status = 0;
+	glGetProgramiv(m_glName, GL_LINK_STATUS, &status);
+
+	if(!status)
+	{
+		GLint infoLen = 0;
+		GLint charsWritten = 0;
+		DynamicArrayAuto<char> infoLogTxt(getAllocator());
+
+		glGetProgramiv(m_glName, GL_INFO_LOG_LENGTH, &infoLen);
+
+		infoLogTxt.create(infoLen + 1);
+
+		glGetProgramInfoLog(m_glName, infoLen, &charsWritten, &infoLogTxt[0]);
+
+		ANKI_LOGE("Link error log follows (vs:%u, fs:%u):\n%s", vert, frag, &infoLogTxt[0]);
+
+		err = ErrorCode::USER_DATA;
+	}
+
+	return err;
+}
+
+} // end namespace anki

+ 46 - 0
src/anki/gr/gl/ShaderProgramImpl.h

@@ -0,0 +1,46 @@
+// Copyright (C) 2009-2016, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <anki/gr/gl/GlObject.h>
+
+namespace anki
+{
+
+/// @addtogroup opengl
+/// @{
+
+static inline void deletePrograms(GLsizei n, const GLuint* progs)
+{
+	ANKI_ASSERT(n == 1);
+	ANKI_ASSERT(progs);
+	glDeleteProgram(*progs);
+}
+
+/// Shader program implementation.
+class ShaderProgramImpl : public GlObject
+{
+public:
+	ShaderProgramImpl(GrManager* manager)
+		: GlObject(manager)
+	{
+	}
+
+	~ShaderProgramImpl()
+	{
+		destroyDeferred(deletePrograms);
+	}
+
+	ANKI_USE_RESULT Error initGraphics(
+		ShaderPtr vert, ShaderPtr tessc, ShaderPtr tesse, ShaderPtr geom, ShaderPtr frag);
+	ANKI_USE_RESULT Error initCompute(ShaderPtr comp);
+
+private:
+	ANKI_USE_RESULT Error link(GLuint vert, GLuint frag);
+};
+/// @}
+
+} // end namespace anki

+ 326 - 0
src/anki/gr/gl/StateTracker.h

@@ -0,0 +1,326 @@
+// Copyright (C) 2009-2016, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <anki/gr/gl/Common.h>
+#include <anki/gr/ShaderProgram.h>
+
+namespace anki
+{
+
+/// @addtogroup opengl
+/// @{
+
+/// Local state tracker. Used to avoid creating command buffer commands.
+class StateTracker
+{
+public:
+	/// @name vert_state
+	/// @{
+	class VertexAttribute
+	{
+	public:
+		U32 m_buffBinding = MAX_U32;
+		PixelFormat m_fmt;
+		PtrSize m_relativeOffset = MAX_PTR_SIZE;
+	};
+
+	Array<VertexAttribute, MAX_VERTEX_ATTRIBUTES> m_attribs;
+
+	template<typename TFunc>
+	void setVertexAttribute(U32 location, U32 buffBinding, const PixelFormat& fmt, PtrSize relativeOffset, TFunc func)
+	{
+		VertexAttribute& attrib = m_attribs[location];
+		if(attrib.m_buffBinding != buffBinding || attrib.m_fmt != fmt || attrib.m_relativeOffset != relativeOffset)
+		{
+			func();
+			attrib.m_buffBinding = buffBinding;
+			attrib.m_fmt = fmt;
+			attrib.m_relativeOffset = relativeOffset;
+		}
+	}
+
+	PtrSize m_indexBuffOffset = MAX_PTR_SIZE;
+	GLenum m_indexType = 0;
+	/// @}
+
+	/// @name input_assembly
+	/// @{
+	U8 m_primitiveRestart = 2;
+
+	template<typename TFunc>
+	void enablePrimitiveRestart(Bool enable, TFunc func)
+	{
+		U enablei = enable ? 1 : 0;
+		if(enablei != m_primitiveRestart)
+		{
+			m_primitiveRestart = enablei;
+			func();
+		}
+	}
+	/// @}
+
+	/// @name viewport_state
+	/// @{
+	Array<U16, 4> m_viewport = {{0, 0, 0, 0}};
+
+	template<typename TFunc>
+	void setViewport(U16 minx, U16 miny, U16 maxx, U16 maxy, TFunc func)
+	{
+		if(m_viewport[0] != minx || m_viewport[1] != miny || m_viewport[2] != maxx || m_viewport[3] != maxy)
+		{
+			m_viewport = {minx, miny, maxx, maxy};
+			func();
+		}
+	}
+	/// @}
+
+	/// @name rasterizer
+	/// @{
+	FillMode m_fillMode = FillMode::COUNT;
+
+	template<typename TFunc>
+	void setFillMode(FillMode mode, TFunc func)
+	{
+		if(m_fillMode != mode)
+		{
+			m_fillMode = mode;
+			func();
+		}
+	}
+
+	FaceSelectionMask m_cullMode = static_cast<FaceSelectionMask>(0);
+
+	template<typename TFunc>
+	void setCullMode(FaceSelectionMask mode, TFunc func)
+	{
+		if(m_cullMode != mode)
+		{
+			m_cullMode = mode;
+			func();
+		}
+	}
+
+	F32 m_polyOffsetFactor = -1.0;
+	F32 m_polyOffsetUnits = -1.0;
+
+	template<typename TFunc>
+	void setPolygonOffset(F32 factor, F32 units, TFunc func)
+	{
+		if(factor != m_polyOffsetFactor || units != m_polyOffsetUnits)
+		{
+			m_polyOffsetFactor = factor;
+			m_polyOffsetUnits = units;
+			func();
+		}
+	}
+	/// @}
+
+	/// @name depth_stencil
+	/// @{
+	Array<StencilOperation, 2> m_stencilFail = {{StencilOperation::COUNT, StencilOperation::COUNT}};
+	Array<StencilOperation, 2> m_stencilPassDepthFail = {{StencilOperation::COUNT, StencilOperation::COUNT}};
+	Array<StencilOperation, 2> m_stencilPassDepthPass = {{StencilOperation::COUNT, StencilOperation::COUNT}};
+
+	template<typename TFunc>
+	void setStencilOperations(FaceSelectionMask face,
+		StencilOperation stencilFail,
+		StencilOperation stencilPassDepthFail,
+		StencilOperation stencilPassDepthPass,
+		TFunc func)
+	{
+		Bool changed = false;
+		if(!!(face & FaceSelectionMask::FRONT)
+			&& (m_stencilFail[0] != stencilFail || m_stencilPassDepthFail[0] != stencilPassDepthFail
+				   || m_stencilPassDepthPass[0] != stencilPassDepthPass))
+		{
+			m_stencilFail[0] = stencilFail;
+			m_stencilPassDepthFail[0] = stencilPassDepthFail;
+			m_stencilPassDepthPass[0] = stencilPassDepthPass;
+			changed = true;
+		}
+
+		if(!!(face & FaceSelectionMask::BACK)
+			&& (m_stencilFail[1] != stencilFail || m_stencilPassDepthFail[1] != stencilPassDepthFail
+				   || m_stencilPassDepthPass[1] != stencilPassDepthPass))
+		{
+			m_stencilFail[1] = stencilFail;
+			m_stencilPassDepthFail[1] = stencilPassDepthFail;
+			m_stencilPassDepthPass[1] = stencilPassDepthPass;
+			changed = true;
+		}
+
+		if(changed)
+		{
+			func();
+		}
+	}
+
+	Array<Bool8, 2> m_glStencilFuncSeparateDirty = {{false, false}};
+	Array<CompareOperation, 2> m_stencilCompare = {{CompareOperation::COUNT, CompareOperation::COUNT}};
+
+	void setStencilCompareFunction(FaceSelectionMask face, CompareOperation comp)
+	{
+		if(!!(face & FaceSelectionMask::FRONT) && m_stencilCompare[0] != comp)
+		{
+			m_stencilCompare[0] = comp;
+			m_glStencilFuncSeparateDirty[0] = true;
+		}
+
+		if(!!(face & FaceSelectionMask::BACK) && m_stencilCompare[1] != comp)
+		{
+			m_stencilCompare[1] = comp;
+			m_glStencilFuncSeparateDirty[1] = true;
+		}
+	}
+
+	Array<U32, 2> m_stencilCompareMask = {{0x969696, 0x969696}};
+
+	void setStencilCompareMask(FaceSelectionMask face, U32 mask)
+	{
+		if(!!(face & FaceSelectionMask::FRONT) && m_stencilCompareMask[0] != mask)
+		{
+			m_stencilCompareMask[0] = mask;
+			m_glStencilFuncSeparateDirty[0] = true;
+		}
+
+		if(!!(face & FaceSelectionMask::BACK) && m_stencilCompareMask[1] != mask)
+		{
+			m_stencilCompareMask[1] = mask;
+			m_glStencilFuncSeparateDirty[1] = true;
+		}
+	}
+
+	Array<U32, 2> m_stencilWriteMask = {{0x969696, 0x969696}};
+
+	template<typename TFunc>
+	void setStencilWriteMask(FaceSelectionMask face, U32 mask, TFunc func)
+	{
+		Bool changed = false;
+		if(!!(face & FaceSelectionMask::FRONT) && m_stencilWriteMask[0] != mask)
+		{
+			m_stencilWriteMask[0] = mask;
+			changed = true;
+		}
+
+		if(!!(face & FaceSelectionMask::BACK) && m_stencilWriteMask[1] != mask)
+		{
+			m_stencilWriteMask[1] = mask;
+			changed = true;
+		}
+
+		if(changed)
+		{
+			func();
+		}
+	}
+
+	Array<U32, 2> m_stencilRef = {{0x969696, 0x969696}};
+
+	void setStencilReference(FaceSelectionMask face, U32 mask)
+	{
+		if(!!(face & FaceSelectionMask::FRONT) && m_stencilRef[0] != mask)
+		{
+			m_stencilRef[0] = mask;
+			m_glStencilFuncSeparateDirty[0] = true;
+		}
+
+		if(!!(face & FaceSelectionMask::BACK) && m_stencilRef[1] != mask)
+		{
+			m_stencilRef[1] = mask;
+			m_glStencilFuncSeparateDirty[1] = true;
+		}
+	}
+
+	Bool8 m_depthWrite = 2;
+
+	template<typename TFunc>
+	void enableDepthWrite(Bool enable, TFunc func)
+	{
+		if(m_depthWrite != enable)
+		{
+			m_depthWrite = enable;
+			func();
+		}
+	}
+
+	CompareOperation m_depthOp = CompareOperation::COUNT;
+
+	template<typename TFunc>
+	void setDepthCompareFunction(CompareOperation op, TFunc func)
+	{
+		if(op != m_depthOp)
+		{
+			m_depthOp = op;
+			func();
+		}
+	}
+	/// @}
+
+	/// @name color
+	/// @{
+	static const ColorBit INVALID_COLOR_MASK = static_cast<ColorBit>(MAX_U8);
+	Array<ColorBit, MAX_COLOR_ATTACHMENTS> m_colorWriteMasks = {
+		{INVALID_COLOR_MASK, INVALID_COLOR_MASK, INVALID_COLOR_MASK, INVALID_COLOR_MASK}};
+
+	template<typename TFunc>
+	void setColorChannelWriteMask(U32 attachment, ColorBit mask, TFunc func)
+	{
+		if(m_colorWriteMasks[attachment] != mask)
+		{
+			m_colorWriteMasks[attachment] = mask;
+			func();
+		}
+	}
+
+	Array<BlendMethod, MAX_COLOR_ATTACHMENTS> m_blendSrcMethod = {
+		{BlendMethod::COUNT, BlendMethod::COUNT, BlendMethod::COUNT, BlendMethod::COUNT}};
+	Array<BlendMethod, MAX_COLOR_ATTACHMENTS> m_blendDstMethod = {
+		{BlendMethod::COUNT, BlendMethod::COUNT, BlendMethod::COUNT, BlendMethod::COUNT}};
+
+	template<typename TFunc>
+	void setBlendMethods(U32 attachment, BlendMethod src, BlendMethod dst, TFunc func)
+	{
+		if(m_blendSrcMethod[attachment] != src || m_blendDstMethod[attachment] != dst)
+		{
+			m_blendSrcMethod[attachment] = src;
+			m_blendDstMethod[attachment] = dst;
+			func();
+		}
+	}
+
+	Array<BlendFunction, MAX_COLOR_ATTACHMENTS> m_blendFuncs = {
+		{BlendFunction::COUNT, BlendFunction::COUNT, BlendFunction::COUNT, BlendFunction::COUNT}};
+
+	template<typename TFunc>
+	void setBlendFunction(U32 attachment, BlendFunction func, TFunc funct)
+	{
+		if(m_blendFuncs[attachment] != func)
+		{
+			m_blendFuncs[attachment] = func;
+			funct();
+		}
+	}
+	/// @}
+
+	/// @resources
+	/// @{
+	U64 m_progUuid = MAX_U64;
+
+	template<typename TFunc>
+	void bindShaderProgram(ShaderProgramPtr prog, TFunc func)
+	{
+		if(prog->getUuid() != m_progUuid)
+		{
+			m_progUuid = prog->getUuid();
+			func();
+		}
+	}
+	/// @}
+};
+/// @}
+
+} // end namespace anki