Browse Source

Pipeline work

Panagiotis Christopoulos Charitos 10 years ago
parent
commit
8c8f237948

+ 1 - 0
include/anki/Gr.h

@@ -21,6 +21,7 @@
 #include "anki/gr/ShaderHandle.h"
 
 #include "anki/gr/FramebufferHandle.h"
+#include "anki/gr/PipelineCommon.h"
 #include "anki/gr/PipelineHandle.h"
 
 #include "anki/gr/CommandBufferHandle.h"

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

@@ -36,6 +36,7 @@ class ShaderImpl;
 class ShaderHandle;
 class PipelineImpl;
 class PipelineHandle;
+struct PipelineInitializer;
 class FramebufferImpl;
 class FramebufferHandle;
 class TextureImpl;

+ 27 - 11
include/anki/gr/Enums.h

@@ -14,7 +14,17 @@ namespace anki {
 /// @addtogroup graphics
 /// @{
 
-// Enums
+enum ColorBit: U8
+{
+	NONE = 0,
+	RED = 1 << 0,
+	GREEN = 1 << 1,
+	BLUE = 1 << 2,
+	ALPHA = 1 << 3,
+	ALL = RED | GREEN | BLUE | ALPHA
+};
+ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(ColorBit, inline)
+
 enum PrimitiveTopology: U8
 {
 	POINTS,
@@ -34,7 +44,6 @@ enum class FillMode: U8
 
 enum class CullMode: U8
 {
-	NONE,
 	FRONT,
 	BACK,
 	FRONT_AND_BACK
@@ -58,12 +67,12 @@ enum class BlendMethod: U8
 	ONE,
 	SRC_COLOR,
 	ONE_MINUS_SRC_COLOR,
-	DEST_COLOR,
-	ONE_MINUS_DEST_COLOR,
+	DST_COLOR,
+	ONE_MINUS_DST_COLOR,
 	SRC_ALPHA,
 	ONE_MINUS_SRC_ALPHA,
-	DEST_ALPHA,
-	ONE_MINUS_DEST_ALPHA,
+	DST_ALPHA,
+	ONE_MINUS_DST_ALPHA,
 	CONSTANT_COLOR,
 	ONE_MINUS_CONSTANT_COLOR,
 	CONSTANT_ALPHA,
@@ -182,21 +191,28 @@ enum class ShaderVariableDataType: U8
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(ShaderVariableDataType, inline)
 
 /// Format for images and vertex attributes.
-struct PixelFormat
+class PixelFormat
 {
+public:
+	ComponentFormat m_components = ComponentFormat::R8G8B8A8;
+	TransformFormat m_transform = TransformFormat::UNORM;
+	Bool8 m_srgb = false;
+
 	PixelFormat() = default;
 
 	PixelFormat(const PixelFormat&) = default;
 
-	PixelFormat(ComponentFormat cf, TransformFormat tf, Bool srgb)
+	PixelFormat(ComponentFormat cf, TransformFormat tf, Bool srgb = false)
 	:	m_components(cf),
 		m_transform(tf),
 		m_srgb(srgb)
 	{}
 
-	ComponentFormat m_components = ComponentFormat::R8G8B8A8;
-	TransformFormat m_transform = TransformFormat::UNORM;
-	Bool8 m_srgb = false;
+	Bool operator==(const PixelFormat& b) const
+	{
+		return m_components == b.m_components && m_transform == b.m_transform
+			&& m_srgb == b.m_srgb;
+	}
 };
 
 /// Occlusion query result bit.

+ 7 - 4
include/anki/gr/PipelineCommon.h

@@ -8,6 +8,7 @@
 
 #include "anki/gr/Common.h"
 #include "anki/gr/ShaderHandle.h"
+#include "anki/gr/PipelineHandle.h"
 
 namespace anki {
 
@@ -53,23 +54,24 @@ struct ViewportStateInfo
 
 struct RasterizerStateInfo
 {
-	FillMode m_fillCode = FillMode::SOLID;
+	FillMode m_fillMode = FillMode::SOLID;
 	CullMode m_cullMode = CullMode::BACK;
 };
 
 struct DepthStencilStateInfo
 {
-	Bool8 m_depthWriteEnabled = false;
+	Bool8 m_depthWriteEnabled = true;
 	DepthCompareFunction m_depthCompareFunction = DepthCompareFunction::LESS;
 	PixelFormat m_format;
 };
 
 struct ColorAttachmentStateInfo
 {
+	PixelFormat m_format;
 	BlendMethod m_srcBlendMethod = BlendMethod::ONE;
 	BlendMethod m_dstBlendMethod = BlendMethod::ZERO;
 	BlendFunction m_blendFunction = BlendFunction::ADD;
-	PixelFormat m_format;
+	ColorBit m_channelWriteMask = ColorBit::ALL;
 };
 
 struct ColorStateInfo
@@ -78,7 +80,6 @@ struct ColorStateInfo
 	Bool8 m_blendEnabled = false;
 	U8 m_colorAttachmentsCount = 0;
 	Array<ColorAttachmentStateInfo, MAX_COLOR_ATTACHMENTS> m_attachments;
-	U8 m_channelWriteMask = 15;
 };
 
 enum class SubStateBit: U16
@@ -94,6 +95,7 @@ enum class SubStateBit: U16
 	ALL = VERTEX | INPUT_ASSEMBLER | TESSELLATION | VIEWPORT | RASTERIZER 
 		| DEPTH | COLOR
 };
+ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(SubStateBit, inline)
 
 /// Pipeline initializer.
 struct PipelineInitializer
@@ -107,6 +109,7 @@ struct PipelineInitializer
 	ColorStateInfo m_color;
 
 	Array<ShaderHandle, 6> m_shaders;
+	PipelineHandle m_templatePipeline;
 	SubStateBit m_definedState = SubStateBit::NONE;
 };
 /// @}

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

@@ -7,7 +7,6 @@
 #define ANKI_GR_PIPELINE_HANDLE_H
 
 #include "anki/gr/GrHandle.h"
-#include "anki/gr/PipelineCommon.h"
 
 namespace anki {
 

+ 11 - 1
include/anki/gr/gl/State.h → include/anki/gr/gl/GlState.h

@@ -7,6 +7,7 @@
 #define ANKI_GR_GL_STATE_H
 
 #include "anki/gr/Common.h"
+#include "anki/gr/PipelineHandle.h"
 
 namespace anki {
 
@@ -23,7 +24,7 @@ enum class GpuVendor: U8
 
 /// Part of the global state. It's essentialy a cache of the state mainly used
 /// for optimizations and other stuff
-class State
+class GlState
 {
 public:
 	I32 m_version = -1; ///< Minor major GL version. Something like 430
@@ -43,6 +44,15 @@ public:
 	GLuint m_crntPpline = 0;
 
 	Array<GLuint, 256> m_texUnits;
+
+	Bool m_primitiveRestartEnabled = false;
+	GLenum m_topology = 0;
+	U32 m_patchControlPointsCount = 0;
+	GLenum m_fillMode = GL_FILL;
+	GLenum m_cullMode = GL_BACK;
+	GLenum m_depthCompareFunction = GL_LESS;
+
+	PipelineHandle m_lastPipeline;
 	/// @}
 
 	/// Call this from the server

+ 46 - 3
include/anki/gr/gl/PipelineImpl.h

@@ -11,11 +11,14 @@
 
 namespace anki {
 
+// Forward
+class GlState;
+
 /// @addtogroup opengl
 /// @{
 
 /// Program pipeline
-class PipelineImpl: public GlObject
+class PipelineImpl: public GlObject, private PipelineInitializer
 {
 public:
 	using Base = GlObject;
@@ -36,15 +39,55 @@ public:
 	void bind();
 
 private:
-	Array<ShaderHandle, 6> m_shaders;
+	struct Attribute
+	{
+		U8 m_compCount = 0;
+		Bool8 m_normalized = false;
+		GLenum m_type = 0;
+	};
+
+	struct Attachment
+	{
+		GLenum m_srcBlendMethod = GL_ONE;
+		GLenum m_dstBlendMethod = GL_ZERO;
+		GLenum m_blendFunction = GL_ADD;
+		Array<Bool8, 4> m_channelWriteMask;
+	};
+
+	Bool m_complete;
+
+	// Cached values
+	Array<Attribute, MAX_VERTEX_ATTRIBUTES> m_attribs;
+	GLenum m_topology = 0;
+	GLenum m_fillMode = 0;
+	GLenum m_cullMode = 0;
+	Bool8 m_depthWrite = false;
+	GLenum m_depthCompareFunction = 0;
+	Array<Attachment, MAX_COLOR_ATTACHMENTS> m_attachments;
 
 	/// Create pipeline object
 	void createPpline();
 
 	/// Attach all the programs
-	void attachProgramsInternal(const Initializer& init);
+	ANKI_USE_RESULT Error createGlPipeline();
 
 	void destroy();
+
+	void initVertexState();
+	void initInputAssemblerState();
+	void initRasterizerState();
+	void initDepthStencilState();
+	void initColorState();
+
+	void setVertexState(GlState& state);
+	void setInputAssemblerState(GlState& state);
+	void setTessellationState(GlState& state);
+	void setRasterizerState(GlState& state);
+	void setDepthStencilState(GlState& state);
+	void setColorState(GlState& state);
+
+	const PipelineImpl* getPipelineForState(const SubStateBit bit) const;
+	const PipelineImpl* chosePipelineForState(const SubStateBit bit) const;
 };
 /// @}
 

+ 4 - 4
include/anki/gr/gl/RenderingThread.h

@@ -7,7 +7,7 @@
 #define ANKI_GR_GL_RENDERING_THREAD_H
 
 #include "anki/gr/CommandBufferHandle.h"
-#include "anki/gr/gl/State.h"
+#include "anki/gr/gl/GlState.h"
 #include "anki/util/Thread.h"
 
 namespace anki {
@@ -28,12 +28,12 @@ public:
 
 	~RenderingThread();
 
-	State& getState()
+	GlState& getState()
 	{
 		return m_state;
 	}
 
-	const State& getState() const
+	const GlState& getState() const
 	{
 		return m_state;
 	}
@@ -95,7 +95,7 @@ private:
 
 	Thread::Id m_serverThreadId;
 	
-	State m_state;
+	GlState m_state;
 	GLuint m_defaultVao;
 
 	/// A special command buffer that is called every time we want to wait for 

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

@@ -257,7 +257,7 @@ void CommandBufferHandle::setViewport(U16 minx, U16 miny, U16 maxx, U16 maxy)
 
 		Error operator()(CommandBufferImpl* commands)
 		{
-			State& state = commands->getManager().getImplementation().
+			GlState& state = commands->getManager().getImplementation().
 				getRenderingThread().getState();
 
 			if(state.m_viewport[0] != m_value[0] 
@@ -355,7 +355,7 @@ void CommandBufferHandle::setBlendFunctions(GLenum sfactor, GLenum dfactor)
 
 		Error operator()(CommandBufferImpl* commands)
 		{
-			State& state = commands->getManager().getImplementation().
+			GlState& state = commands->getManager().getImplementation().
 				getRenderingThread().getState();
 
 			if(state.m_blendSfunc != m_sfactor 

+ 2 - 2
src/gr/gl/State.cpp → src/gr/gl/GlState.cpp

@@ -3,7 +3,7 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#include "anki/gr/gl/State.h"
+#include "anki/gr/gl/GlState.h"
 #include "anki/gr/gl/BufferImpl.h"
 #include "anki/util/Logger.h"
 #include <algorithm>
@@ -78,7 +78,7 @@ void oglMessagesCallback(GLenum source,
 #endif
 
 //==============================================================================
-void State::init()
+void GlState::init()
 {
 	// GL version
 	GLint major, minor;

+ 1 - 1
src/gr/gl/GrManager.cpp

@@ -44,7 +44,7 @@ void GrManager::swapBuffers()
 //==============================================================================
 PtrSize GrManager::getBufferOffsetAlignment(GLenum target) const
 {
-	const State& state = m_impl->getRenderingThread().getState();
+	const GlState& state = m_impl->getRenderingThread().getState();
 
 	if(target == GL_UNIFORM_BUFFER)
 	{

+ 2 - 1
src/gr/gl/PipelineHandle.cpp

@@ -4,6 +4,7 @@
 // http://www.anki3d.org/LICENSE
 
 #include "anki/gr/PipelineHandle.h"
+#include "anki/gr/PipelineCommon.h"
 #include "anki/gr/gl/PipelineImpl.h"
 #include "anki/gr/gl/DeferredDeleter.h"
 
@@ -49,7 +50,7 @@ public:
 
 	Error operator()(CommandBufferImpl* commands)
 	{
-		State& state = commands->getManager().getImplementation().
+		GlState& state = commands->getManager().getImplementation().
 			getRenderingThread().getState();
 
 		auto name = m_ppline.get().getGlName();

+ 452 - 31
src/gr/gl/PipelineImpl.cpp

@@ -5,6 +5,8 @@
 
 #include "anki/gr/gl/PipelineImpl.h"
 #include "anki/gr/gl/ShaderImpl.h"
+#include "anki/gr/gl/GrManagerImpl.h"
+#include "anki/gr/gl/RenderingThread.h"
 #include "anki/util/Logger.h"
 
 namespace anki {
@@ -29,12 +31,155 @@ static GLenum computeGlShaderType(const ShaderType idx, GLbitfield* bit)
 	return gltype[enumToType(idx)];
 }
 
+//==============================================================================
+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;
+}
+
 //==============================================================================
 Error PipelineImpl::create(const Initializer& init)
+{
+	static_cast<Initializer&>(*this) = init;
+
+	// Check if complete
+	m_complete = true;
+
+	if(m_templatePipeline.isCreated())
+	{
+		// If there is a template it should always be complete
+		SubStateBit mask = 
+			m_definedState | m_templatePipeline.get().m_definedState;
+		ANKI_ASSERT(mask == SubStateBit::ALL);
+	}
+	else if(m_definedState != SubStateBit::ALL)
+	{
+		m_complete = false;
+	}
+
+	ANKI_CHECK(createGlPipeline());
+	initVertexState();
+	initInputAssemblerState();
+	initRasterizerState();
+	initDepthStencilState();
+	initColorState();
+
+	return ErrorCode::NONE;
+}
+
+//==============================================================================
+void PipelineImpl::destroy()
+{
+	if(m_glName)
+	{
+		glDeleteProgramPipelines(1, &m_glName);
+		m_glName = 0;
+	}
+}
+
+//==============================================================================
+Error PipelineImpl::createGlPipeline()
 {
 	Error err = ErrorCode::NONE;
 
-	attachProgramsInternal(init);
+	// Do checks
+	U mask = 0;
+	U count = 6;
+	while(count-- != 0)
+	{
+		const ShaderHandle& shader = m_shaders[count];
+		if(shader.isCreated())
+		{
+			ANKI_ASSERT(count == enumToType(shader.get().getType()));
+			mask |= 1 << count;
+		}
+	}
+
+	if(mask & (1 << 5))
+	{
+		// Is compute
+
+		ANKI_ASSERT((mask & (1 << 5)) == (1 << 5) 
+			&& "Compute should be alone in the pipeline");
+	}
+	else
+	{
+		const U fragVert = (1 << 0) | (1 << 4);
+		ANKI_ASSERT((mask & fragVert) && "Should contain vert and frag");
+		(void)fragVert;
+
+		const U tess = (1 << 1) | (1 << 2);
+		if((mask & tess) != 0)
+		{
+			ANKI_ASSERT(((mask & tess) == 0x6)
+				&& "Should set both the tessellation shaders");
+		}
+	}
 
 	// Create and attach programs
 	glGenProgramPipelines(1, &m_glName);
@@ -83,60 +228,336 @@ Error PipelineImpl::create(const Initializer& init)
 }
 
 //==============================================================================
-void PipelineImpl::destroy()
+void PipelineImpl::bind()
 {
-	if(m_glName)
+#if 1
+	ANKI_ASSERT(isCreated());
+	glBindProgramPipeline(m_glName);
+#else
+	ANKI_ASSERT(m_complete && "Should be complete");
+
+	// Get last pipeline
+	auto& state = 
+		getManager().getImplementation().getRenderingThread().getState();
+
+	const PipelineImpl* last = nullptr;
+	const PipelineImpl* lastTempl = nullptr;
+	if(state.m_lastPipeline.isCreated())
 	{
-		glDeleteProgramPipelines(1, &m_glName);
-		m_glName = 0;
+		last = &state.m_lastPipeline.get();
+
+		if(last->m_templatePipeline.isCreated())
+		{
+			lastTempl = &last->m_templatePipeline.get();
+		}
 	}
+	
+	/*if(!lastPpline.isCreated() || !templPpline.isCreated())
+	{
+		// Bind state
+	}*/
+#endif
 }
 
 //==============================================================================
-void PipelineImpl::attachProgramsInternal(const Initializer& init)
+void PipelineImpl::initVertexState()
 {
-	U mask = 0;
-	U count = 6;
-	while(count-- != 0)
+	for(U i = 0; i < m_vertex.m_attributeCount; ++i)
 	{
-		const ShaderHandle& shader = init.m_shaders[count];
-		if(shader.isCreated())
+		const VertexAttributeBinding& binding = m_vertex.m_attributes[i];
+		ANKI_ASSERT(binding.m_format.m_srgb == false);
+		Attribute& cache = m_attribs[i];
+
+		// Component count
+		if(binding.m_format == PixelFormat(
+			ComponentFormat::R32G32, TransformFormat::FLOAT))
 		{
-			ANKI_ASSERT(count == enumToType(shader.get().getType()));
-			m_shaders[count] = shader;
-			mask |= 1 << count;
+			cache.m_compCount = 2;
+			cache.m_type = GL_FLOAT;
+			cache.m_normalized = false;
+		}
+		else if(binding.m_format == PixelFormat(
+			ComponentFormat::R32G32B32, TransformFormat::FLOAT))
+		{
+			cache.m_compCount = 3;
+			cache.m_type = GL_FLOAT;
+			cache.m_normalized = false;
+		}
+		else if(binding.m_format == PixelFormat(
+			ComponentFormat::R10G10B10A2, TransformFormat::SNORM))
+		{
+			cache.m_compCount = 4;
+			cache.m_type = GL_INT_2_10_10_10_REV;
+			cache.m_normalized = true;
+		}
+		else
+		{
+			ANKI_ASSERT(0 && "TODO");
 		}
 	}
+}
 
-	// Check what we attached
-	//
-	if(mask & (1 << 5))
+//==============================================================================
+void PipelineImpl::initInputAssemblerState()
+{
+	switch(m_inputAssembler.m_topology)
 	{
-		// Is compute
+	case POINTS:
+		m_topology = GL_POINTS;
+		break;
+	case LINES:
+		m_topology = GL_LINES;
+		break;
+	case LINE_STIP:
+		m_topology = GL_LINE_STRIP;
+		break;
+	case TRIANGLES:
+		m_topology = GL_TRIANGLES;
+		break;
+	case TRIANGLE_STRIP:
+		m_topology = GL_TRIANGLE_STRIP;
+		break;
+	case PATCHES:
+		m_topology = GL_PATCHES;
+		break;
+	default:
+		ANKI_ASSERT(0);
+	}
+}
 
-		ANKI_ASSERT((mask & (1 << 5)) == (1 << 5) 
-			&& "Compute should be alone in the pipeline");
+//==============================================================================
+void PipelineImpl::initRasterizerState()
+{
+	switch(m_rasterizer.m_fillMode)
+	{
+	case FillMode::POINTS:
+		m_fillMode = GL_POINT;
+		break;
+	case FillMode::WIREFRAME:
+		m_fillMode = GL_LINE;
+		break;
+	case FillMode::SOLID:
+		m_fillMode = GL_FILL;
+		break;
+	default:
+		ANKI_ASSERT(0);
 	}
-	else
+
+	switch(m_rasterizer.m_cullMode)
 	{
-		const U fragVert = (1 << 0) | (1 << 4);
-		ANKI_ASSERT((mask & fragVert) && "Should contain vert and frag");
-		(void)fragVert;
+	case CullMode::FRONT:
+		m_cullMode = GL_FRONT;
+		break;
+	case CullMode::BACK:
+		m_cullMode = GL_BACK;
+		break;
+	case CullMode::FRONT_AND_BACK:
+		m_cullMode = GL_FRONT_AND_BACK;
+		break;
+	default:
+		ANKI_ASSERT(0);
+	}
+}
 
-		const U tess = (1 << 1) | (1 << 2);
-		if((mask & tess) != 0)
+//==============================================================================
+void PipelineImpl::initDepthStencilState()
+{
+	switch(m_depthStencil.m_depthCompareFunction)
+	{
+	case DepthCompareFunction::ALWAYS:
+		m_depthCompareFunction = GL_ALWAYS;
+		break;
+	case DepthCompareFunction::LESS:
+		m_depthCompareFunction = GL_LESS;
+		break;
+	case DepthCompareFunction::LESS_EQUAL:
+		m_depthCompareFunction = GL_LEQUAL;
+		break;
+	case DepthCompareFunction::GREATER:
+		m_depthCompareFunction = GL_GREATER;
+		break;
+	case DepthCompareFunction::GREATER_EQUAL:
+		m_depthCompareFunction = GL_GEQUAL;
+		break;
+	case DepthCompareFunction::NOT_EQUAL:
+		m_depthCompareFunction = GL_NOTEQUAL;
+		break;
+	case DepthCompareFunction::NEVER:
+		m_depthCompareFunction = GL_NEVER;
+		break;
+	default:
+		ANKI_ASSERT(0);
+	}
+}
+
+//==============================================================================
+void PipelineImpl::initColorState()
+{
+	for(U i = 0; i < m_color.m_colorAttachmentsCount; ++i)
+	{
+		Attachment& out = m_attachments[i];
+		const ColorAttachmentStateInfo& in = m_color.m_attachments[i];
+
+		out.m_srcBlendMethod = convertBlendMethod(in.m_srcBlendMethod);
+		out.m_dstBlendMethod = convertBlendMethod(in.m_dstBlendMethod);
+
+		switch(in.m_blendFunction)
 		{
-			ANKI_ASSERT(((mask & tess) == 0x6)
-				&& "Should set both the tessellation shaders");
+		case BlendFunction::ADD:
+			out.m_blendFunction = GL_FUNC_ADD;
+			break;
+		case BlendFunction::SUBTRACT:
+			out.m_blendFunction = GL_FUNC_SUBTRACT;
+			break;
+		case BlendFunction::REVERSE_SUBTRACT:
+			out.m_blendFunction = GL_FUNC_REVERSE_SUBTRACT;
+			break;
+		case BlendFunction::MIN:
+			out.m_blendFunction = GL_MIN;
+			break;
+		case BlendFunction::MAX:
+			out.m_blendFunction = GL_MAX;
+			break;
+		default:
+			ANKI_ASSERT(0);
 		}
+
+		out.m_channelWriteMask[0] = in.m_channelWriteMask | ColorBit::RED;
+		out.m_channelWriteMask[1] = in.m_channelWriteMask | ColorBit::GREEN;
+		out.m_channelWriteMask[2] = in.m_channelWriteMask | ColorBit::BLUE;
+		out.m_channelWriteMask[3] = in.m_channelWriteMask | ColorBit::ALPHA;
 	}
 }
 
 //==============================================================================
-void PipelineImpl::bind()
+void PipelineImpl::setVertexState(GlState&)
 {
-	ANKI_ASSERT(isCreated());
-	glBindProgramPipeline(m_glName);
+	for(U i = 0; i < m_vertex.m_attributeCount; ++i)
+	{
+		const Attribute& attrib = m_attribs[i];
+		ANKI_ASSERT(attrib.m_type);
+		glVertexAttribFormat(i, attrib.m_compCount, attrib.m_type, 
+			attrib.m_normalized, m_vertex.m_attributes[i].m_offset);
+
+		glVertexAttribBinding(i, m_vertex.m_attributes[i].m_binding);
+	}
+}
+
+//==============================================================================
+void PipelineImpl::setInputAssemblerState(GlState& state)
+{
+	if(m_inputAssembler.m_primitiveRestartEnabled 
+		!= state.m_primitiveRestartEnabled)
+	{
+		if(m_inputAssembler.m_primitiveRestartEnabled)
+		{
+			glEnable(GL_PRIMITIVE_RESTART);
+		}
+		else
+		{
+			glDisable(GL_PRIMITIVE_RESTART);
+		}
+
+		state.m_primitiveRestartEnabled = 
+			m_inputAssembler.m_primitiveRestartEnabled;
+	}
+
+	state.m_topology = m_topology;
+}
+
+//==============================================================================
+void PipelineImpl::setTessellationState(GlState& state)
+{
+	if(m_tessellation.m_patchControlPointsCount 
+		!= state.m_patchControlPointsCount)
+	{
+		glPatchParameteri(GL_PATCH_VERTICES, 
+			m_tessellation.m_patchControlPointsCount);
+
+		state.m_patchControlPointsCount = 
+			m_tessellation.m_patchControlPointsCount;
+	}
+}
+
+//==============================================================================
+void PipelineImpl::setRasterizerState(GlState& state)
+{
+	if(m_fillMode != state.m_fillMode)
+	{
+		glPolygonMode(GL_FRONT_AND_BACK, m_fillMode);
+		state.m_fillMode = m_fillMode;
+	}
+
+	if(m_cullMode != state.m_cullMode)
+	{
+		glCullFace(m_cullMode);
+		state.m_cullMode = m_cullMode;
+	}
+}
+
+//==============================================================================
+void PipelineImpl::setDepthStencilState(GlState& state)
+{
+	if(m_depthCompareFunction != state.m_depthCompareFunction)
+	{
+		if(m_depthCompareFunction == GL_ALWAYS)
+		{
+			glDisable(GL_DEPTH_TEST);
+		}
+		else
+		{
+			glEnable(GL_DEPTH_TEST);
+		}
+
+		glDepthFunc(m_depthCompareFunction);
+
+		state.m_depthCompareFunction = m_depthCompareFunction;
+	}
+}
+
+//==============================================================================
+void PipelineImpl::setColorState(GlState&)
+{
+	for(U i = 0; i < m_color.m_colorAttachmentsCount; ++i)
+	{
+		const Attachment& att = m_attachments[i];
+
+		glBlendFunci(i, att.m_srcBlendMethod, att.m_dstBlendMethod);
+		glBlendEquationi(i, att.m_blendFunction);
+		glColorMaski(i, att.m_channelWriteMask[0], att.m_channelWriteMask[1], 
+			att.m_channelWriteMask[2], att.m_channelWriteMask[3]);
+	}
+}
+
+//==============================================================================
+const PipelineImpl* getPipelineForState(const SubStateBit bit) const
+{
+	PipelineImpl* out = this;
+
+	if(m_templatePipeline.isCreated() && !(m_definedState | bit))
+	{
+		// Template pipeline has the defined state
+		out = &m_templatePipeline.get();
+	}
+
+	return out;
+}
+
+//==============================================================================
+const PipelineImpl* PipelineImpl::chosePipelineForState(
+	const SubStateBit bit, const PipelineImpl& crntBoundPipeline) const 
+{
+	const PipelineImpl* out = nullptr;
+
+	// Get previously bound pipeline template
+	const PipelineImpl* last = nullptr;
+	if(crntBoundPipeline.m_templatePipeline.isCreated() 
+		&& !(crntBoundPipeline.m_definedState | bit))
+	{
+		last = &crntBoundPipeline.m_templatePipeline.get();
+	}
+
+	if(last)
 }
 
 } // end namespace anki