Explorar o código

Merge pull request #16 from godlikepanos/gr_simplification

new GR interface. Only works with GL for now
Panagiotis Christopoulos Charitos %!s(int64=9) %!d(string=hai) anos
pai
achega
d3ff3ca0fb
Modificáronse 100 ficheiros con 3676 adicións e 4168 borrados
  1. 3 1
      shaders/Irradiance.frag.glsl
  2. 6 7
      shaders/Is.frag.glsl
  3. 1 2
      src/anki/Gr.h
  4. 2 3
      src/anki/core/Trace.cpp
  5. 2 3
      src/anki/core/Trace.h
  6. 1 1
      src/anki/gr/CMakeLists.txt
  7. 95 10
      src/anki/gr/CommandBuffer.h
  8. 12 28
      src/anki/gr/Common.h
  9. 16 4
      src/anki/gr/Enums.h
  10. 14 0
      src/anki/gr/GrObject.h
  11. 0 268
      src/anki/gr/Pipeline.h
  12. 0 90
      src/anki/gr/ResourceGroup.h
  13. 0 101
      src/anki/gr/ResourceGroup.inl.h
  14. 43 0
      src/anki/gr/ShaderProgram.h
  15. 0 9
      src/anki/gr/common/Misc.cpp
  16. 8 4
      src/anki/gr/common/Misc.h
  17. 7 0
      src/anki/gr/gl/BufferImpl.h
  18. 986 115
      src/anki/gr/gl/CommandBuffer.cpp
  19. 152 172
      src/anki/gr/gl/CommandBufferImpl.cpp
  20. 14 48
      src/anki/gr/gl/CommandBufferImpl.h
  21. 133 0
      src/anki/gr/gl/Common.cpp
  22. 105 0
      src/anki/gr/gl/Common.h
  23. 17 0
      src/anki/gr/gl/FramebufferImpl.h
  24. 1 29
      src/anki/gr/gl/GlState.cpp
  25. 4 43
      src/anki/gr/gl/GlState.h
  26. 0 62
      src/anki/gr/gl/Pipeline.cpp
  27. 0 624
      src/anki/gr/gl/PipelineImpl.cpp
  28. 0 112
      src/anki/gr/gl/PipelineImpl.h
  29. 0 64
      src/anki/gr/gl/ResourceGroup.cpp
  30. 0 384
      src/anki/gr/gl/ResourceGroupImpl.cpp
  31. 0 86
      src/anki/gr/gl/ResourceGroupImpl.h
  32. 21 21
      src/anki/gr/gl/Sampler.cpp
  33. 117 0
      src/anki/gr/gl/ShaderProgram.cpp
  34. 78 0
      src/anki/gr/gl/ShaderProgramImpl.cpp
  35. 46 0
      src/anki/gr/gl/ShaderProgramImpl.h
  36. 615 0
      src/anki/gr/gl/StateTracker.h
  37. 24 47
      src/anki/renderer/Bloom.cpp
  38. 14 4
      src/anki/renderer/Bloom.h
  39. 10 7
      src/anki/renderer/Clusterer.h
  40. 14 43
      src/anki/renderer/DebugDrawer.cpp
  41. 1 9
      src/anki/renderer/DebugDrawer.h
  42. 30 33
      src/anki/renderer/DepthDownscale.cpp
  43. 3 3
      src/anki/renderer/DepthDownscale.h
  44. 25 32
      src/anki/renderer/DownscaleBlur.cpp
  45. 4 5
      src/anki/renderer/DownscaleBlur.h
  46. 88 62
      src/anki/renderer/Drawer.cpp
  47. 2 7
      src/anki/renderer/Drawer.h
  48. 44 68
      src/anki/renderer/Fs.cpp
  49. 3 5
      src/anki/renderer/Fs.h
  50. 34 39
      src/anki/renderer/FsUpscale.cpp
  51. 4 2
      src/anki/renderer/FsUpscale.h
  52. 79 106
      src/anki/renderer/Ir.cpp
  53. 5 7
      src/anki/renderer/Ir.h
  54. 68 138
      src/anki/renderer/Is.cpp
  55. 4 15
      src/anki/renderer/Is.h
  56. 52 80
      src/anki/renderer/Lf.cpp
  57. 3 5
      src/anki/renderer/Lf.h
  58. 5 14
      src/anki/renderer/MainRenderer.cpp
  59. 1 2
      src/anki/renderer/MainRenderer.h
  60. 7 26
      src/anki/renderer/Ms.cpp
  61. 0 3
      src/anki/renderer/Ms.h
  62. 32 65
      src/anki/renderer/Pps.cpp
  63. 1 3
      src/anki/renderer/Pps.h
  64. 12 20
      src/anki/renderer/Renderer.cpp
  65. 15 5
      src/anki/renderer/Renderer.h
  66. 17 14
      src/anki/renderer/Sm.cpp
  67. 7 23
      src/anki/renderer/Sm.h
  68. 43 59
      src/anki/renderer/Smaa.cpp
  69. 5 4
      src/anki/renderer/Smaa.h
  70. 40 90
      src/anki/renderer/Ssao.cpp
  71. 3 8
      src/anki/renderer/Ssao.h
  72. 15 26
      src/anki/renderer/Sslf.cpp
  73. 1 2
      src/anki/renderer/Sslf.h
  74. 0 205
      src/anki/renderer/Tiler.cpp
  75. 0 57
      src/anki/renderer/Tiler.h
  76. 28 28
      src/anki/renderer/Tm.cpp
  77. 6 9
      src/anki/renderer/Tm.h
  78. 39 35
      src/anki/renderer/Volumetric.cpp
  79. 3 2
      src/anki/renderer/Volumetric.h
  80. 10 44
      src/anki/resource/Material.cpp
  81. 6 7
      src/anki/resource/Material.h
  82. 34 64
      src/anki/resource/Model.cpp
  83. 43 9
      src/anki/resource/Model.h
  84. 2 5
      src/anki/resource/ParticleEmitterResource.cpp
  85. 2 2
      src/anki/resource/ParticleEmitterResource.h
  86. 1 1
      src/anki/scene/CMakeLists.txt
  87. 0 8
      src/anki/scene/LensFlareComponent.cpp
  88. 0 7
      src/anki/scene/LensFlareComponent.h
  89. 16 5
      src/anki/scene/ModelNode.cpp
  90. 19 41
      src/anki/scene/ParticleEmitter.cpp
  91. 1 2
      src/anki/scene/ParticleEmitter.h
  92. 0 2
      src/anki/scene/RenderComponent.cpp
  93. 61 45
      src/anki/scene/RenderComponent.h
  94. 2 2
      src/anki/scene/SoftwareRasterizer.cpp
  95. 9 5
      src/anki/ui/UiInterfaceImpl.cpp
  96. 0 2
      src/anki/ui/UiInterfaceImpl.h
  97. 18 0
      src/anki/util/Thread.h
  98. 49 0
      src/anki/util/ThreadPosix.cpp
  99. 96 208
      tests/gr/Gr.cpp
  100. 17 1
      tools/format_source.sh

+ 3 - 1
shaders/Irradiance.frag.glsl

@@ -7,6 +7,8 @@
 
 #include "shaders/Common.glsl"
 
+const float INDIRECT_BUMP = 2.5; // A sort of hack
+
 layout(location = 0) in vec2 in_uv;
 layout(location = 0) out vec3 out_color;
 
@@ -53,7 +55,7 @@ void main()
 				vec3 col = texture(u_envTex, vec4(r, texArrIdx)).rgb;
 
 				float lambert = max(0.0, dot(r, ri));
-				outCol += col * lambert;
+				outCol += col * lambert * INDIRECT_BUMP;
 				weight += lambert;
 			}
 		}

+ 6 - 7
shaders/Is.frag.glsl

@@ -238,27 +238,26 @@ void main()
 #endif
 
 #if 0
-	out_color = diffCol;
-	uint count = scount;
+	count = scount;
 	if(count == 0)
 	{
-		out_color = vec3(1.0, 0.0, 0.0);
+		out_color = vec3(0.0, 0.0, 0.0);
 	}
 	else if(count == 1)
 	{
-		out_color = vec3(0.0, 1.0, 0.0);
+		out_color = vec3(1.0, 0.0, 0.0);
 	}
 	else if(count == 2)
 	{
-		out_color = vec3(0.0, 0.0, 1.0);
+		out_color = vec3(0.0, 1.0, 0.0);
 	}
 	else if(count == 3)
 	{
-		out_color = vec3(1.0, 0.0, 1.0);
+		out_color = vec3(0.0, 0.0, 1.0);
 	}
 	else
 	{
-		out_color = vec3(1.0, 1.0, 0.0);
+		out_color = vec3(1.0, 1.0, 1.0);
 	}
 #endif
 }

+ 1 - 2
src/anki/Gr.h

@@ -9,11 +9,10 @@
 #include <anki/gr/Texture.h>
 #include <anki/gr/Sampler.h>
 #include <anki/gr/Shader.h>
+#include <anki/gr/ShaderProgram.h>
 #include <anki/gr/Framebuffer.h>
-#include <anki/gr/Pipeline.h>
 #include <anki/gr/CommandBuffer.h>
 #include <anki/gr/OcclusionQuery.h>
-#include <anki/gr/ResourceGroup.h>
 #include <anki/gr/GrManager.h>
 #include <anki/gr/GrObjectCache.h>
 

+ 2 - 3
src/anki/core/Trace.cpp

@@ -44,9 +44,8 @@ static Array<const char*, U(TraceCounterType::COUNT)> counterNames = {{"GR_DRAWC
 	"GR_DYNAMIC_UNIFORMS_SIZE",
 	"GR_DYNAMIC_STORAGE_SIZE",
 	"GR_VERTICES",
-	"GR_PIPELINES_CREATED",
-	"GR_PIPELINE_BINDS_SKIPPED",
-	"GR_PIPELINE_BINDS_HAPPENED",
+	"GL_PROGS_SKIPPED",
+	"VK_PIPELINES_CREATED",
 	"VK_PIPELINE_BARRIERS",
 	"VK_CMD_BUFFER_CREATE",
 	"VK_FENCE_CREATE",

+ 2 - 3
src/anki/core/Trace.h

@@ -62,9 +62,8 @@ enum class TraceCounterType
 	GR_DYNAMIC_UNIFORMS_SIZE,
 	GR_DYNAMIC_STORAGE_SIZE,
 	GR_VERTICES,
-	GR_PIPELINES_CREATED,
-	GR_PIPELINE_BINDS_SKIPPED,
-	GR_PIPELINE_BINDS_HAPPENED,
+	GL_PROGS_SKIPPED,
+	VK_PIPELINES_CREATED,
 	VK_PIPELINE_BARRIERS,
 	VK_CMD_BUFFER_CREATE,
 	VK_FENCE_CREATE,

+ 1 - 1
src/anki/gr/CMakeLists.txt

@@ -18,4 +18,4 @@ else()
 endif()
 
 add_library(ankigr ${ANKI_GR_SOURCES} ${ANKI_GR_BACKEND_SOURCES})
-target_link_libraries(ankigr ankiutil ankicore ${EXTRA_LIBS})
+target_link_libraries(ankigr ankiutil ${EXTRA_LIBS})

+ 95 - 10
src/anki/gr/CommandBuffer.h

@@ -141,12 +141,51 @@ public:
 	/// @name State manipulation
 	/// @{
 
+	/// Bind vertex buffer.
+	void bindVertexBuffer(U32 binding, BufferPtr buff, PtrSize offset, PtrSize stride);
+
+	/// Bind transient vertex buffer.
+	void bindVertexBuffer(U32 binding, const TransientMemoryToken& token, PtrSize stride);
+
+	/// 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 setPrimitiveRestart(Bool enable);
+
 	/// Set the viewport.
 	void setViewport(U16 minx, U16 miny, U16 maxx, U16 maxy);
 
-	/// Set depth offset and units.
+	/// Enable scissor test.
+	void setScissorTest(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);
 
+	/// 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.
 	void setStencilCompareMask(FaceSelectionMask face, U32 mask);
 
@@ -156,28 +195,74 @@ public:
 	/// Set the stencil reference.
 	void setStencilReference(FaceSelectionMask face, U32 ref);
 
-	/// Bind pipeline.
-	void bindPipeline(PipelinePtr ppline);
+	/// Enable/disable depth write.
+	void setDepthWrite(Bool enable);
+
+	/// Set depth compare function.
+	void setDepthCompareFunction(CompareOperation op);
+
+	/// Enable/disable alpha to coverage.
+	void setAlphaToCoverage(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);
 
 	/// Begin renderpass.
 	void beginRenderPass(FramebufferPtr fb);
 
 	/// End renderpass.
 	void endRenderPass();
-
-	/// Bind resources.
-	void bindResourceGroup(ResourceGroupPtr rc, U slot, const TransientMemoryInfo* dynInfo);
 	/// @}
 
 	/// @name Jobs
 	/// @{
-	void drawElements(U32 count, U32 instanceCount = 1, U32 firstIndex = 0, U32 baseVertex = 0, U32 baseInstance = 0);
+	void drawElements(PrimitiveTopology topology,
+		U32 count,
+		U32 instanceCount = 1,
+		U32 firstIndex = 0,
+		U32 baseVertex = 0,
+		U32 baseInstance = 0);
 
-	void drawArrays(U32 count, U32 instanceCount = 1, U32 first = 0, U32 baseInstance = 0);
+	void drawArrays(PrimitiveTopology topology, U32 count, U32 instanceCount = 1, U32 first = 0, U32 baseInstance = 0);
 
-	void drawElementsIndirect(U32 drawCount, PtrSize offset, BufferPtr indirectBuff);
+	void drawElementsIndirect(PrimitiveTopology topology, U32 drawCount, PtrSize offset, BufferPtr indirectBuff);
 
-	void drawArraysIndirect(U32 drawCount, PtrSize offset, BufferPtr indirectBuff);
+	void drawArraysIndirect(PrimitiveTopology topology, U32 drawCount, PtrSize offset, BufferPtr indirectBuff);
 
 	void dispatchCompute(U32 groupCountX, U32 groupCountY, U32 groupCountZ);
 

+ 12 - 28
src/anki/gr/Common.h

@@ -22,9 +22,7 @@ class GrManagerImpl;
 class TextureInitInfo;
 class SamplerInitInfo;
 class GrManagerInitInfo;
-class PipelineInitInfo;
 class FramebufferInitInfo;
-class ResourceGroupInitInfo;
 
 /// @addtogroup graphics
 /// @{
@@ -43,10 +41,9 @@ ANKI_GR_CLASS(Texture)
 ANKI_GR_CLASS(Sampler)
 ANKI_GR_CLASS(CommandBuffer)
 ANKI_GR_CLASS(Shader)
-ANKI_GR_CLASS(Pipeline)
 ANKI_GR_CLASS(Framebuffer)
 ANKI_GR_CLASS(OcclusionQuery)
-ANKI_GR_CLASS(ResourceGroup)
+ANKI_GR_CLASS(ShaderProgram)
 
 #undef ANKI_GR_CLASS
 
@@ -58,21 +55,6 @@ ANKI_GR_CLASS(ResourceGroup)
 	friend class GenericPoolAllocator;                                                                                 \
 	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
 enum class GpuVendor : U8
 {
@@ -191,6 +173,17 @@ enum class TransientMemoryTokenLifetime : U8
 /// Token that gets returned when requesting for memory to write to a resource.
 class TransientMemoryToken
 {
+public:
+	operator Bool() const
+	{
+		return m_range != 0;
+	}
+
+	Bool operator==(const TransientMemoryToken& b) const
+	{
+		return m_offset == b.m_offset && m_range == b.m_range && m_lifetime == b.m_lifetime && m_usage == b.m_usage;
+	}
+
 anki_internal:
 	PtrSize m_offset = 0;
 	PtrSize m_range = 0;
@@ -208,15 +201,6 @@ anki_internal:
 	}
 };
 
-/// Struct to help update the offset of the dynamic buffers.
-class TransientMemoryInfo
-{
-public:
-	Array<TransientMemoryToken, MAX_UNIFORM_BUFFER_BINDINGS> m_uniformBuffers;
-	Array<TransientMemoryToken, MAX_STORAGE_BUFFER_BINDINGS> m_storageBuffers;
-	Array<TransientMemoryToken, MAX_VERTEX_ATTRIBUTES> m_vertexBuffers;
-};
-
 /// Compute max number of mipmaps for a 2D texture.
 inline U computeMaxMipmapCount2d(U w, U h, U minSizeOfLastMip = 1)
 {

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

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

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

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

+ 0 - 268
src/anki/gr/Pipeline.h

@@ -1,268 +0,0 @@
-// 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>
-#include <anki/gr/Shader.h>
-#include <anki/util/Hash.h>
-
-namespace anki
-{
-
-/// @addtogroup graphics
-/// @{
-
-class VertexBinding
-{
-public:
-	PtrSize m_stride; ///< Vertex stride.
-	VertexStepRate m_stepRate = VertexStepRate::VERTEX;
-
-	Bool operator==(const VertexBinding& b) const
-	{
-		return m_stride == b.m_stride && m_stepRate == b.m_stepRate;
-	}
-
-	Bool operator!=(const VertexBinding& b) const
-	{
-		return !(*this == b);
-	}
-};
-
-class VertexAttributeBinding
-{
-public:
-	PixelFormat m_format;
-	PtrSize m_offset = 0;
-	U8 m_binding = 0;
-
-	Bool operator==(const VertexAttributeBinding& b) const
-	{
-		return m_format == b.m_format && m_offset == b.m_offset && m_binding == b.m_binding;
-	}
-
-	Bool operator!=(const VertexAttributeBinding& b) const
-	{
-		return !(*this == b);
-	}
-};
-
-class VertexStateInfo
-{
-public:
-	U8 m_bindingCount = 0;
-	Array<VertexBinding, MAX_VERTEX_ATTRIBUTES> m_bindings;
-	U8 m_attributeCount = 0;
-	Array<VertexAttributeBinding, MAX_VERTEX_ATTRIBUTES> m_attributes;
-
-	Bool operator==(const VertexStateInfo& b) const
-	{
-		if(m_bindingCount != b.m_bindingCount || m_attributeCount != b.m_attributeCount)
-		{
-			return false;
-		}
-
-		for(U i = 0; i < m_bindingCount; ++i)
-		{
-			if(m_bindings[i] != b.m_bindings[i])
-			{
-				return false;
-			}
-		}
-
-		for(U i = 0; i < m_attributeCount; ++i)
-		{
-			if(m_attributes[i] != b.m_attributes[i])
-			{
-				return false;
-			}
-		}
-
-		return true;
-	}
-
-	Bool operator!=(const VertexStateInfo& b) const
-	{
-		return !(*this == b);
-	}
-};
-
-class InputAssemblerStateInfo
-{
-public:
-	PrimitiveTopology m_topology = PrimitiveTopology::TRIANGLES;
-	Bool8 m_primitiveRestartEnabled = false;
-
-	Bool operator==(const InputAssemblerStateInfo& b) const
-	{
-		return m_topology == b.m_topology && m_primitiveRestartEnabled == b.m_primitiveRestartEnabled;
-	}
-
-	Bool operator!=(const InputAssemblerStateInfo& b) const
-	{
-		return !(*this == b);
-	}
-};
-
-class TessellationStateInfo
-{
-public:
-	U32 m_patchControlPointCount = 3;
-};
-
-class ViewportStateInfo
-{
-public:
-	Bool8 m_scissorEnabled = false;
-};
-
-class RasterizerStateInfo
-{
-public:
-	FillMode m_fillMode = FillMode::SOLID;
-	FaceSelectionMask m_cullMode = FaceSelectionMask::BACK;
-};
-
-class StencilStateInfo
-{
-public:
-	StencilOperation m_stencilFailOperation = StencilOperation::KEEP;
-	StencilOperation m_stencilPassDepthFailOperation = StencilOperation::KEEP;
-	StencilOperation m_stencilPassDepthPassOperation = StencilOperation::KEEP;
-	CompareOperation m_compareFunction = CompareOperation::ALWAYS;
-};
-
-/// Depth stencil state.
-/// To disable depth test set m_depthWriteEnabled to false and m_depthCompareFunction to always.
-/// To disable stencil test leave m_stencilFront and m_stencilBack as is.
-class DepthStencilStateInfo
-{
-public:
-	// Depth
-	Bool8 m_depthWriteEnabled = true;
-	CompareOperation m_depthCompareFunction = CompareOperation::LESS;
-
-	// Stencil
-	StencilStateInfo m_stencilFront;
-	StencilStateInfo m_stencilBack;
-
-	// Common
-	PixelFormat m_format;
-
-	Bool isInUse() const
-	{
-		return m_format.m_components != ComponentFormat::NONE;
-	}
-};
-
-class ColorAttachmentStateInfo
-{
-public:
-	PixelFormat m_format;
-
-	BlendMethod m_srcBlendMethod = BlendMethod::ONE;
-	BlendMethod m_dstBlendMethod = BlendMethod::ZERO;
-	BlendFunction m_blendFunction = BlendFunction::ADD;
-	ColorBit m_channelWriteMask = ColorBit::ALL;
-};
-
-class ColorStateInfo
-{
-public:
-	Bool8 m_alphaToCoverageEnabled = false;
-	U8 m_attachmentCount = 0;
-	Array<ColorAttachmentStateInfo, MAX_COLOR_ATTACHMENTS> m_attachments;
-};
-
-enum class PipelineSubStateBit : U16
-{
-	NONE = 0,
-	VERTEX = 1 << 0,
-	INPUT_ASSEMBLER = 1 << 1,
-	TESSELLATION = 1 << 2,
-	VIEWPORT = 1 << 3,
-	RASTERIZER = 1 << 4,
-	DEPTH_STENCIL = 1 << 5,
-	COLOR = 1 << 6,
-	SHADERS = 1 << 7,
-	ALL = VERTEX | INPUT_ASSEMBLER | TESSELLATION | VIEWPORT | RASTERIZER | DEPTH_STENCIL | COLOR | SHADERS
-};
-ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(PipelineSubStateBit, inline)
-
-/// Only the state part of PipelineInitInfo. It's separate for easy hashing.
-class PipelineInitInfoState
-{
-public:
-	PipelineInitInfoState()
-	{
-		// Do a special construction. The state will be hashed and the padding may contain garbage. With this trick
-		// zero the padding
-		memset(this, 0, sizeof(*this));
-
-#define ANKI_CONSTRUCT_AND_ZERO_PADDING(memb_) new(&memb_) decltype(memb_)()
-
-		ANKI_CONSTRUCT_AND_ZERO_PADDING(m_vertex);
-		ANKI_CONSTRUCT_AND_ZERO_PADDING(m_inputAssembler);
-		ANKI_CONSTRUCT_AND_ZERO_PADDING(m_tessellation);
-		ANKI_CONSTRUCT_AND_ZERO_PADDING(m_viewport);
-		ANKI_CONSTRUCT_AND_ZERO_PADDING(m_rasterizer);
-		ANKI_CONSTRUCT_AND_ZERO_PADDING(m_depthStencil);
-		ANKI_CONSTRUCT_AND_ZERO_PADDING(m_color);
-
-#undef ANKI_CONSTRUCT_AND_ZERO_PADDING
-	}
-
-	VertexStateInfo m_vertex;
-	InputAssemblerStateInfo m_inputAssembler;
-	TessellationStateInfo m_tessellation;
-	ViewportStateInfo m_viewport;
-	RasterizerStateInfo m_rasterizer;
-	DepthStencilStateInfo m_depthStencil;
-	ColorStateInfo m_color;
-};
-
-/// Pipeline initializer.
-class PipelineInitInfo : public PipelineInitInfoState
-{
-public:
-	Array<ShaderPtr, U(ShaderType::COUNT)> m_shaders;
-
-	U64 computeHash() const
-	{
-		U64 h = anki::computeHash(static_cast<const PipelineInitInfoState*>(this), sizeof(PipelineInitInfoState));
-
-		Array<U64, U(ShaderType::COUNT)> uuids;
-		for(U i = 0; i < m_shaders.getSize(); ++i)
-		{
-			uuids[i] = (m_shaders[i].isCreated()) ? m_shaders[i]->getUuid() : 0;
-		}
-
-		return appendHash(&uuids[0], sizeof(uuids), h);
-	}
-};
-
-/// Graphics and compute pipeline. Contains the static state.
-class Pipeline : public GrObject
-{
-	ANKI_GR_OBJECT
-
-anki_internal:
-	UniquePtr<PipelineImpl> m_impl;
-
-	static const GrObjectType CLASS_TYPE = GrObjectType::PIPELINE;
-
-	/// Construct.
-	Pipeline(GrManager* manager, U64 hash, GrObjectCache* cache);
-
-	/// Destroy.
-	~Pipeline();
-
-	/// Create.
-	void init(const PipelineInitInfo& init);
-};
-/// @}
-
-} // end namespace anki

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

@@ -1,90 +0,0 @@
-// 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>
-#include <anki/gr/Texture.h>
-#include <anki/gr/Sampler.h>
-#include <anki/gr/Buffer.h>
-
-namespace anki
-{
-
-/// @addtogroup graphics
-/// @{
-
-/// Texture/Sampler Binding.
-class TextureBinding
-{
-public:
-	TexturePtr m_texture;
-	SamplerPtr m_sampler; ///< Use it to override texture's sampler.
-	TextureUsageBit m_usage = TextureUsageBit::SAMPLED_FRAGMENT;
-	DepthStencilAspectMask m_aspect = DepthStencilAspectMask::DEPTH; ///< Relevant only for depth stencil textures.
-};
-
-/// Buffer binding info.
-class BufferBinding
-{
-public:
-	BufferPtr m_buffer;
-	PtrSize m_offset = 0;
-	PtrSize m_range = 0; ///< If zero it means the whole buffer.
-	Bool m_uploadedMemory = false;
-	BufferUsageBit m_usage = BufferUsageBit::NONE;
-};
-
-/// Image binding info.
-class ImageBinding
-{
-public:
-	TexturePtr m_texture;
-	U8 m_level = 0;
-	TextureUsageBit m_usage = TextureUsageBit::IMAGE_COMPUTE_READ_WRITE;
-};
-
-/// Resource group initializer.
-class ResourceGroupInitInfo
-{
-public:
-	Array<TextureBinding, MAX_TEXTURE_BINDINGS> m_textures;
-	Array<BufferBinding, MAX_UNIFORM_BUFFER_BINDINGS> m_uniformBuffers;
-	Array<BufferBinding, MAX_STORAGE_BUFFER_BINDINGS> m_storageBuffers;
-	Array<ImageBinding, MAX_IMAGE_BINDINGS> m_images;
-	Array<BufferBinding, MAX_VERTEX_ATTRIBUTES> m_vertexBuffers;
-	BufferBinding m_indexBuffer;
-	I8 m_indexSize = -1; ///< Index size in bytes. 2 or 4
-
-	U64 computeHash() const;
-
-private:
-	void appendBufferBinding(const BufferBinding& b, U64 arr[], U& count) const;
-};
-
-/// Resource group.
-class ResourceGroup : public GrObject
-{
-	ANKI_GR_OBJECT
-
-anki_internal:
-	UniquePtr<ResourceGroupImpl> m_impl;
-
-	static const GrObjectType CLASS_TYPE = GrObjectType::RESOURCE_GROUP;
-
-	/// Construct.
-	ResourceGroup(GrManager* manager, U64 hash, GrObjectCache* cache);
-
-	/// Destroy.
-	~ResourceGroup();
-
-	/// Create.
-	void init(const ResourceGroupInitInfo& init);
-};
-/// @}
-
-} // end namespace anki
-
-#include <anki/gr/ResourceGroup.inl.h>

+ 0 - 101
src/anki/gr/ResourceGroup.inl.h

@@ -1,101 +0,0 @@
-// 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/ResourceGroup.h>
-
-namespace anki
-{
-
-inline void ResourceGroupInitInfo::appendBufferBinding(const BufferBinding& b, U64 arr[], U& count) const
-{
-	arr[count++] = (b.m_buffer) ? b.m_buffer->getUuid() : 0;
-	arr[count++] = b.m_offset;
-	arr[count++] = b.m_range;
-	arr[count++] = b.m_uploadedMemory;
-	arr[count++] = static_cast<U64>(b.m_usage);
-}
-
-inline U64 ResourceGroupInitInfo::computeHash() const
-{
-	const U TEX_NUMBERS = MAX_TEXTURE_BINDINGS * 4;
-	const U BUFF_NUMBERS = (MAX_UNIFORM_BUFFER_BINDINGS + MAX_STORAGE_BUFFER_BINDINGS + MAX_VERTEX_ATTRIBUTES + 1) * 5;
-	const U IMAGE_NUMBERS = MAX_IMAGE_BINDINGS * 3;
-	const U INDEX_SIZE_NUMBERS = 1;
-	Array<U64, TEX_NUMBERS + BUFF_NUMBERS + IMAGE_NUMBERS + INDEX_SIZE_NUMBERS> numbers;
-	U count = 0;
-
-	for(const auto& tex : m_textures)
-	{
-		if(tex.m_texture)
-		{
-			numbers[count++] = tex.m_texture->getUuid();
-			if(tex.m_sampler)
-			{
-				numbers[count++] = tex.m_sampler->getUuid();
-			}
-			numbers[count++] = static_cast<U64>(tex.m_usage);
-			numbers[count++] = static_cast<U64>(tex.m_aspect);
-		}
-		else
-		{
-			break;
-		}
-	}
-
-	for(const auto& b : m_uniformBuffers)
-	{
-		if(b.m_buffer || b.m_uploadedMemory)
-		{
-			appendBufferBinding(b, &numbers[0], count);
-		}
-		else
-		{
-			break;
-		}
-	}
-	for(const auto& b : m_storageBuffers)
-	{
-		if(b.m_buffer || b.m_uploadedMemory)
-		{
-			appendBufferBinding(b, &numbers[0], count);
-		}
-		else
-		{
-			break;
-		}
-	}
-	for(const auto& b : m_vertexBuffers)
-	{
-		if(b.m_buffer || b.m_uploadedMemory)
-		{
-			appendBufferBinding(b, &numbers[0], count);
-		}
-		else
-		{
-			break;
-		}
-	}
-	appendBufferBinding(m_indexBuffer, &numbers[0], count);
-
-	for(const auto& img : m_images)
-	{
-		if(img.m_texture)
-		{
-			numbers[count++] = img.m_texture->getUuid();
-			numbers[count++] = img.m_level;
-			numbers[count++] = static_cast<U64>(img.m_usage);
-		}
-		else
-		{
-			break;
-		}
-	}
-
-	numbers[count++] = static_cast<U64>(m_indexSize);
-
-	return anki::computeHash(&numbers[0], count * sizeof(U64), 458);
-}
-
-} // end namespace anki

+ 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

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

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

+ 8 - 4
src/anki/gr/common/Misc.h

@@ -10,9 +10,6 @@
 namespace anki
 {
 
-// Forward
-class StencilStateInfo;
-
 enum class TransientBufferType
 {
 	UNIFORM,
@@ -87,6 +84,13 @@ PtrSize computeSurfaceSize(U width, U height, const PixelFormat& fmt);
 /// Compute the size of the volume.
 PtrSize computeVolumeSize(U width, U height, U depth, const PixelFormat& fmt);
 
-Bool stencilTestDisabled(const StencilStateInfo& inf);
+inline Bool stencilTestDisabled(StencilOperation stencilFail,
+	StencilOperation stencilPassDepthFail,
+	StencilOperation stencilPassDepthPass,
+	CompareOperation compare)
+{
+	return stencilFail == StencilOperation::KEEP && stencilPassDepthFail == StencilOperation::KEEP
+		&& stencilPassDepthPass == StencilOperation::KEEP && compare == CompareOperation::ALWAYS;
+}
 
 } // end namespace anki

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

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

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 986 - 115
src/anki/gr/gl/CommandBuffer.cpp


+ 152 - 172
src/anki/gr/gl/CommandBufferImpl.cpp

@@ -9,9 +9,9 @@
 #include <anki/gr/gl/GlState.h>
 #include <anki/gr/gl/Error.h>
 
-#include <anki/gr/gl/ResourceGroupImpl.h>
 #include <anki/gr/OcclusionQuery.h>
 #include <anki/gr/gl/OcclusionQueryImpl.h>
+#include <anki/gr/Buffer.h>
 #include <anki/gr/gl/BufferImpl.h>
 
 #include <anki/util/Logger.h>
@@ -96,241 +96,221 @@ GrAllocator<U8> CommandBufferImpl::getAllocator() const
 	return m_manager->getAllocator();
 }
 
-class BindResourcesCommand final : public GlCommand
+void CommandBufferImpl::flushDrawcall(CommandBuffer& cmdb)
 {
-public:
-	ResourceGroupPtr m_rc;
-	TransientMemoryInfo m_info;
-	U8 m_slot;
-
-	BindResourcesCommand(ResourceGroupPtr rc, U8 slot, const TransientMemoryInfo* info)
-		: m_rc(rc)
-		, m_slot(slot)
-	{
-		if(info)
-		{
-			m_info = *info;
-		}
-	}
+	ANKI_ASSERT(!!(m_flags & CommandBufferFlag::GRAPHICS_WORK));
 
-	Error operator()(GlState& state)
+	//
+	// Set default state
+	//
+	if(ANKI_UNLIKELY(m_state.m_mayContainUnsetState))
 	{
-		ANKI_TRACE_START_EVENT(GL_BIND_RESOURCES);
-		m_rc->m_impl->bind(m_slot, m_info, state);
-		ANKI_TRACE_STOP_EVENT(GL_BIND_RESOURCES);
-		return ErrorCode::NONE;
-	}
-};
+		m_state.m_mayContainUnsetState = false;
 
-void CommandBufferImpl::bindResourceGroup(ResourceGroupPtr rc, U slot, const TransientMemoryInfo* info)
-{
-	ANKI_ASSERT(rc.isCreated());
+		if(m_state.m_primitiveRestart == 2)
+		{
+			cmdb.setPrimitiveRestart(false);
+		}
 
-	pushBackNewCommand<BindResourcesCommand>(rc, slot, info);
-}
+		if(m_state.m_fillMode == FillMode::COUNT)
+		{
+			cmdb.setFillMode(FillMode::SOLID);
+		}
 
-void CommandBufferImpl::drawElements(U32 count, U32 instanceCount, U32 firstIndex, U32 baseVertex, U32 baseInstance)
-{
-	class DrawElementsCommand : public GlCommand
-	{
-	public:
-		DrawElementsIndirectInfo m_info;
+		if(m_state.m_cullMode == static_cast<FaceSelectionMask>(0))
+		{
+			cmdb.setCullMode(FaceSelectionMask::BACK);
+		}
 
-		DrawElementsCommand(const DrawElementsIndirectInfo& info)
-			: m_info(info)
+		if(m_state.m_polyOffsetFactor == -1.0)
 		{
+			cmdb.setPolygonOffset(0.0, 0.0);
 		}
 
-		Error operator()(GlState& state)
+		for(U i = 0; i < 2; ++i)
 		{
-			GLenum indicesType = 0;
-			switch(state.m_indexSize)
+			FaceSelectionMask face = (i == 0) ? FaceSelectionMask::FRONT : FaceSelectionMask::BACK;
+
+			if(m_state.m_stencilFail[i] == StencilOperation::COUNT)
 			{
-			case 2:
-				indicesType = GL_UNSIGNED_SHORT;
-				break;
-			case 4:
-				indicesType = GL_UNSIGNED_INT;
-				break;
-			default:
-				ANKI_ASSERT(0);
-				break;
-			};
-
-			state.flushVertexState();
-			state.flushStencilState();
-			glDrawElementsInstancedBaseVertexBaseInstance(state.m_topology,
-				m_info.m_count,
-				indicesType,
-				(const void*)(PtrSize)(m_info.m_firstIndex * state.m_indexSize),
-				m_info.m_instanceCount,
-				m_info.m_baseVertex,
-				m_info.m_baseInstance);
-
-			ANKI_TRACE_INC_COUNTER(GR_DRAWCALLS, 1);
-			ANKI_TRACE_INC_COUNTER(GR_VERTICES, m_info.m_instanceCount * m_info.m_count);
+				cmdb.setStencilOperations(face, StencilOperation::KEEP, StencilOperation::KEEP, StencilOperation::KEEP);
+			}
 
-			return ErrorCode::NONE;
+			if(m_state.m_stencilCompare[i] == CompareOperation::COUNT)
+			{
+				cmdb.setStencilCompareFunction(face, CompareOperation::ALWAYS);
+			}
+
+			if(m_state.m_stencilCompareMask[i] == StateTracker::DUMMY_STENCIL_MASK)
+			{
+				cmdb.setStencilCompareMask(face, MAX_U32);
+			}
+
+			if(m_state.m_stencilWriteMask[i] == StateTracker::DUMMY_STENCIL_MASK)
+			{
+				cmdb.setStencilWriteMask(face, MAX_U32);
+			}
+
+			if(m_state.m_stencilRef[i] == StateTracker::DUMMY_STENCIL_MASK)
+			{
+				cmdb.setStencilReference(face, 0);
+			}
 		}
-	};
 
-	ANKI_ASSERT(m_dbg.m_insideRenderPass);
-	DrawElementsIndirectInfo info(count, instanceCount, firstIndex, baseVertex, baseInstance);
+		if(m_state.m_depthWrite == 2)
+		{
+			cmdb.setDepthWrite(true);
+		}
 
-	checkDrawcall();
-	pushBackNewCommand<DrawElementsCommand>(info);
-}
+		if(m_state.m_depthOp == CompareOperation::COUNT)
+		{
+			cmdb.setDepthCompareFunction(CompareOperation::LESS);
+		}
 
-void CommandBufferImpl::drawArrays(U32 count, U32 instanceCount, U32 first, U32 baseInstance)
-{
-	class DrawArraysCommand final : public GlCommand
+		for(U i = 0; i < MAX_COLOR_ATTACHMENTS; ++i)
+		{
+			if(m_state.m_colorWriteMasks[i] == StateTracker::INVALID_COLOR_MASK)
+			{
+				cmdb.setColorChannelWriteMask(i, ColorBit::ALL);
+			}
+
+			if(m_state.m_blendSrcMethod[i] == BlendMethod::COUNT)
+			{
+				cmdb.setBlendMethods(i, BlendMethod::ONE, BlendMethod::ZERO);
+			}
+
+			if(m_state.m_blendFuncs[i] == BlendFunction::COUNT)
+			{
+				cmdb.setBlendFunction(i, BlendFunction::ADD);
+			}
+		}
+	}
+
+	//
+	// Fire commands to change some state
+	//
+	class StencilCmd final : public GlCommand
 	{
 	public:
-		DrawArraysIndirectInfo m_info;
-
-		DrawArraysCommand(const DrawArraysIndirectInfo& info)
-			: m_info(info)
+		GLenum m_face;
+		GLenum m_func;
+		GLint m_ref;
+		GLuint m_compareMask;
+
+		StencilCmd(GLenum face, GLenum func, GLint ref, GLuint mask)
+			: m_face(face)
+			, m_func(func)
+			, m_ref(ref)
+			, m_compareMask(mask)
 		{
 		}
 
-		Error operator()(GlState& state)
+		Error operator()(GlState&)
 		{
-			state.flushVertexState();
-			state.flushStencilState();
-			glDrawArraysInstancedBaseInstance(
-				state.m_topology, m_info.m_first, m_info.m_count, m_info.m_instanceCount, m_info.m_baseInstance);
-
-			ANKI_TRACE_INC_COUNTER(GR_DRAWCALLS, 1);
+			glStencilFuncSeparate(m_face, m_func, m_ref, m_compareMask);
 			return ErrorCode::NONE;
 		}
 	};
 
-	ANKI_ASSERT(m_dbg.m_insideRenderPass);
-	DrawArraysIndirectInfo info(count, instanceCount, first, baseInstance);
+	for(U i = 0; i < 2; ++i)
+	{
+		if(m_state.m_glStencilFuncSeparateDirty[i])
+		{
+			pushBackNewCommand<StencilCmd>(GL_FRONT + i,
+				convertCompareOperation(m_state.m_stencilCompare[i]),
+				m_state.m_stencilRef[i],
+				m_state.m_stencilCompareMask[i]);
 
-	checkDrawcall();
-	pushBackNewCommand<DrawArraysCommand>(info);
-}
+			m_state.m_glStencilFuncSeparateDirty[i] = false;
+		}
+	}
 
-void CommandBufferImpl::drawElementsIndirect(U32 drawCount, PtrSize offset, BufferPtr indirectBuff)
-{
-	class DrawElementsIndirectCommand final : public GlCommand
+	class DepthTestCmd final : public GlCommand
 	{
 	public:
-		U32 m_drawCount;
-		PtrSize m_offset;
-		BufferPtr m_buff;
-
-		DrawElementsIndirectCommand(U32 drawCount, PtrSize offset, BufferPtr buff)
-			: m_drawCount(drawCount)
-			, m_offset(offset)
-			, m_buff(buff)
+		Bool8 m_enable;
+
+		DepthTestCmd(Bool enable)
+			: m_enable(enable)
 		{
-			ANKI_ASSERT(drawCount > 0);
-			ANKI_ASSERT((m_offset % 4) == 0);
 		}
 
-		Error operator()(GlState& state)
+		Error operator()(GlState&)
 		{
-			state.flushVertexState();
-			state.flushStencilState();
-			const BufferImpl& buff = *m_buff->m_impl;
-
-			ANKI_ASSERT(m_offset + sizeof(DrawElementsIndirectInfo) * m_drawCount <= buff.m_size);
-
-			glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buff.getGlName());
-
-			GLenum indicesType = 0;
-			switch(state.m_indexSize)
+			if(m_enable)
+			{
+				glEnable(GL_DEPTH_TEST);
+			}
+			else
 			{
-			case 2:
-				indicesType = GL_UNSIGNED_SHORT;
-				break;
-			case 4:
-				indicesType = GL_UNSIGNED_INT;
-				break;
-			default:
-				ANKI_ASSERT(0);
-				break;
-			};
-
-			glMultiDrawElementsIndirect(state.m_topology,
-				indicesType,
-				numberToPtr<void*>(m_offset),
-				m_drawCount,
-				sizeof(DrawElementsIndirectInfo));
-
-			glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0);
+				glDisable(GL_DEPTH_TEST);
+			}
 			return ErrorCode::NONE;
 		}
 	};
 
-	checkDrawcall();
-	pushBackNewCommand<DrawElementsIndirectCommand>(drawCount, offset, indirectBuff);
-}
+	if(m_state.maybeEnableDepthTest())
+	{
+		pushBackNewCommand<DepthTestCmd>(m_state.m_depthTestEnabled);
+	}
 
-void CommandBufferImpl::drawArraysIndirect(U32 drawCount, PtrSize offset, BufferPtr indirectBuff)
-{
-	class DrawArraysIndirectCommand final : public GlCommand
+	class StencilTestCmd final : public GlCommand
 	{
 	public:
-		U32 m_drawCount;
-		PtrSize m_offset;
-		BufferPtr m_buff;
-
-		DrawArraysIndirectCommand(U32 drawCount, PtrSize offset, BufferPtr buff)
-			: m_drawCount(drawCount)
-			, m_offset(offset)
-			, m_buff(buff)
+		Bool8 m_enable;
+
+		StencilTestCmd(Bool enable)
+			: m_enable(enable)
 		{
-			ANKI_ASSERT(drawCount > 0);
-			ANKI_ASSERT((m_offset % 4) == 0);
 		}
 
-		Error operator()(GlState& state)
+		Error operator()(GlState&)
 		{
-			state.flushVertexState();
-			state.flushStencilState();
-			const BufferImpl& buff = *m_buff->m_impl;
-
-			ANKI_ASSERT(m_offset + sizeof(DrawArraysIndirectInfo) * m_drawCount <= buff.m_size);
-
-			glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buff.getGlName());
-
-			glMultiDrawArraysIndirect(
-				state.m_topology, numberToPtr<void*>(m_offset), m_drawCount, sizeof(DrawArraysIndirectInfo));
-
-			glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0);
+			if(m_enable)
+			{
+				glEnable(GL_STENCIL_TEST);
+			}
+			else
+			{
+				glDisable(GL_STENCIL_TEST);
+			}
 			return ErrorCode::NONE;
 		}
 	};
 
-	checkDrawcall();
-	pushBackNewCommand<DrawArraysIndirectCommand>(drawCount, offset, indirectBuff);
-}
-
-void CommandBufferImpl::dispatchCompute(U32 groupCountX, U32 groupCountY, U32 groupCountZ)
-{
-	ANKI_ASSERT(!m_dbg.m_insideRenderPass);
+	if(m_state.maybeEnableStencilTest())
+	{
+		pushBackNewCommand<StencilTestCmd>(m_state.m_stencilTestEnabled);
+	}
 
-	class DispatchCommand final : public GlCommand
+	class BlendCmd final : public GlCommand
 	{
 	public:
-		Array<U32, 3> m_size;
+		Bool8 m_enable;
 
-		DispatchCommand(U32 x, U32 y, U32 z)
-			: m_size({{x, y, z}})
+		BlendCmd(Bool enable)
+			: m_enable(enable)
 		{
 		}
 
 		Error operator()(GlState&)
 		{
-			glDispatchCompute(m_size[0], m_size[1], m_size[2]);
+			if(m_enable)
+			{
+				glEnable(GL_BLEND);
+			}
+			else
+			{
+				glDisable(GL_BLEND);
+			}
 			return ErrorCode::NONE;
 		}
 	};
 
-	pushBackNewCommand<DispatchCommand>(groupCountX, groupCountY, groupCountZ);
+	if(m_state.maybeEnableBlend())
+	{
+		pushBackNewCommand<BlendCmd>(m_state.m_enableBlend);
+	}
 }
 
 } // end namespace anki

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

@@ -6,6 +6,7 @@
 #pragma once
 
 #include <anki/gr/CommandBuffer.h>
+#include <anki/gr/gl/StateTracker.h>
 #include <anki/util/Assert.h>
 #include <anki/util/Allocator.h>
 
@@ -41,17 +42,19 @@ class CommandBufferImpl
 public:
 	using InitHints = CommandBufferInitHints;
 
-#if ANKI_ASSERTS_ENABLED
-	class StateSet
-	{
-	public:
-		Bool m_viewport = false;
-		Bool m_polygonOffset = false;
-		Bool m_insideRenderPass = false;
-		Bool m_secondLevel = false;
-	} m_dbg;
+	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
 	CommandBufferImpl(GrManager* manager)
 		: m_manager(manager)
@@ -109,52 +112,15 @@ public:
 		return m_firstCommand == nullptr;
 	}
 
-	void bindResourceGroup(ResourceGroupPtr rc, U slot, const TransientMemoryInfo* info);
-
-	void drawElements(U32 count, U32 instanceCount = 1, U32 firstIndex = 0, U32 baseVertex = 0, U32 baseInstance = 0);
-
-	void drawArrays(U32 count, U32 instanceCount = 1, U32 first = 0, U32 baseInstance = 0);
-
-	void drawElementsIndirect(U32 drawCount, PtrSize offset, BufferPtr indirectBuff);
-
-	void drawArraysIndirect(U32 drawCount, PtrSize offset, BufferPtr indirectBuff);
-
-	void drawElementsConditional(OcclusionQueryPtr query,
-		U32 count,
-		U32 instanceCount = 1,
-		U32 firstIndex = 0,
-		U32 baseVertex = 0,
-		U32 baseInstance = 0);
-
-	void drawArraysConditional(
-		OcclusionQueryPtr query, U32 count, U32 instanceCount = 1, U32 first = 0, U32 baseInstance = 0);
-
-	void dispatchCompute(U32 groupCountX, U32 groupCountY, U32 groupCountZ);
-
 	Bool isSecondLevel() const
 	{
 		return !!(m_flags & CommandBufferFlag::SECOND_LEVEL);
 	}
 
-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 flushDrawcall(CommandBuffer& cmdb);
 
+private:
 	void destroy();
-
-	void checkDrawcall() const
-	{
-		ANKI_ASSERT(m_dbg.m_viewport == true);
-		ANKI_ASSERT(m_dbg.m_polygonOffset == true);
-	}
 };
 
 template<typename TCommand, typename... TArgs>

+ 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

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

@@ -57,6 +57,111 @@ inline GLenum convertFaceMode(FaceSelectionMask in)
 }
 
 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;
+}
+
+inline GLenum convertPrimitiveTopology(PrimitiveTopology ak)
+{
+	GLenum out;
+	switch(ak)
+	{
+	case PrimitiveTopology::POINTS:
+		out = GL_POINTS;
+		break;
+	case PrimitiveTopology::LINES:
+		out = GL_LINES;
+		break;
+	case PrimitiveTopology::LINE_STIP:
+		out = GL_LINE_STRIP;
+		break;
+	case PrimitiveTopology::TRIANGLES:
+		out = GL_TRIANGLES;
+		break;
+	case PrimitiveTopology::TRIANGLE_STRIP:
+		out = GL_TRIANGLE_STRIP;
+		break;
+	case PrimitiveTopology::PATCHES:
+		out = GL_PATCHES;
+		break;
+	default:
+		ANKI_ASSERT(0);
+		out = 0;
+	}
+
+	return out;
+}
 /// @}
 
 } // end namespace anki

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

@@ -35,6 +35,23 @@ public:
 	/// Bind it to the state. Call it in rendering thread
 	void bind(const GlState& state);
 
+	U getColorBufferCount() const
+	{
+		return m_in.m_colorAttachmentCount;
+	}
+
+	Bool hasDepthBuffer() const
+	{
+		return m_in.m_depthStencilAttachment.m_texture.isCreated()
+			&& !!(m_in.m_depthStencilAttachment.m_aspect & DepthStencilAspectMask::DEPTH);
+	}
+
+	Bool hasStencilBuffer() const
+	{
+		return m_in.m_depthStencilAttachment.m_texture.isCreated()
+			&& !!(m_in.m_depthStencilAttachment.m_aspect & DepthStencilAspectMask::STENCIL);
+	}
+
 private:
 	FramebufferInitInfo m_in;
 

+ 1 - 29
src/anki/gr/gl/GlState.cpp

@@ -129,6 +129,7 @@ void GlState::initRenderThread()
 	// Set some GL state
 	glEnable(GL_PROGRAM_POINT_SIZE);
 	glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
+	glEnable(GL_CULL_FACE);
 
 	// Create default VAO
 	glGenVertexArrays(1, &m_defaultVao);
@@ -139,9 +140,6 @@ void GlState::initRenderThread()
 	{
 		glEnableVertexAttribArray(i);
 	}
-
-	// Other
-	memset(&m_vertexBindingStrides[0], 0, sizeof(m_vertexBindingStrides));
 }
 
 void GlState::destroy()
@@ -149,30 +147,4 @@ void GlState::destroy()
 	glDeleteVertexArrays(1, &m_defaultVao);
 }
 
-void GlState::flushVertexState()
-{
-	if(m_vertBindingsDirty)
-	{
-		m_vertBindingsDirty = false;
-
-		glBindVertexBuffers(
-			0, m_vertBindingCount, &m_vertBuffNames[0], &m_vertBuffOffsets[0], &m_vertexBindingStrides[0]);
-	}
-}
-
-void GlState::flushStencilState()
-{
-	if(m_glStencilFuncSeparateDirtyMask & 1)
-	{
-		glStencilFuncSeparate(GL_FRONT, m_stencilCompareFunc[0], m_stencilRef[0], m_stencilCompareMask[0]);
-		m_glStencilFuncSeparateDirtyMask &= ~1u;
-	}
-
-	if(m_glStencilFuncSeparateDirtyMask & 2)
-	{
-		glStencilFuncSeparate(GL_BACK, m_stencilCompareFunc[1], m_stencilRef[1], m_stencilCompareMask[1]);
-		m_glStencilFuncSeparateDirtyMask &= ~2u;
-	}
-}
-
 } // end namespace anki

+ 4 - 43
src/anki/gr/gl/GlState.h

@@ -26,54 +26,18 @@ public:
 	GpuVendor m_gpu = GpuVendor::UNKNOWN;
 	Bool8 m_registerMessages = false;
 
-	GLuint m_defaultVao;
+	GLuint m_defaultVao = 0;
 
-	/// @name Cached state
+	/// @name FB
 	/// @{
-	Array<U16, 4> m_viewport = {{0, 0, 0, 0}};
-
-	GLenum m_blendSfunc = GL_ONE;
-	GLenum m_blendDfunc = GL_ZERO;
-	/// @}
-
-	/// @name Pipeline/resource group state
-	/// @{
-	U64 m_lastPplineBoundUuid = MAX_U64;
-
-	Array<GLuint, MAX_VERTEX_ATTRIBUTES> m_vertBuffNames;
-	Array<GLintptr, MAX_VERTEX_ATTRIBUTES> m_vertBuffOffsets;
-	Array<GLsizei, MAX_VERTEX_ATTRIBUTES> m_vertexBindingStrides;
-	U8 m_vertBindingCount = 0;
-	Bool8 m_vertBindingsDirty = true;
-
-	GLenum m_topology = 0;
-	U8 m_indexSize = 4;
-
-	class
-	{
-	public:
-		U64 m_vertex = 0;
-		U64 m_inputAssembler = 0;
-		U64 m_tessellation = 0;
-		U64 m_viewport = 0;
-		U64 m_rasterizer = 0;
-		U64 m_depthStencil = 0;
-		U64 m_color = 0;
-	} m_stateHashes;
-
 	Array2d<Bool, MAX_COLOR_ATTACHMENTS, 4> m_colorWriteMasks = {{{{true, true, true, true}},
 		{{true, true, true, true}},
 		{{true, true, true, true}},
 		{{true, true, true, true}}}};
-	Bool m_depthWriteMask = true;
 
-	// Stencil
-	Array<GLenum, 2> m_stencilCompareFunc = {{GL_ALWAYS, GL_ALWAYS}}; ///< Pipeline sets it
-	Array<U32, 2> m_stencilRef = {{0, 0}}; ///< CommandBuffer sets it
-	Array<U32, 2> m_stencilCompareMask = {{MAX_U32, MAX_U32}}; ///< CommandBuffer sets it
-	U8 m_glStencilFuncSeparateDirtyMask = 0;
+	Bool8 m_depthWriteMask = true;
 
-	Array<U32, 2> m_stencilWriteMask = {{MAX_U32, MAX_U32}}; ///< Framebuffer wants that.
+	Array<U32, 2> m_stencilWriteMask = {{MAX_U32, MAX_U32}};
 	/// @}
 
 	GlState(GrManager* manager)
@@ -89,9 +53,6 @@ public:
 
 	/// Call this from the rendering thread.
 	void destroy();
-
-	void flushVertexState();
-	void flushStencilState();
 };
 /// @}
 

+ 0 - 62
src/anki/gr/gl/Pipeline.cpp

@@ -1,62 +0,0 @@
-// 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/Pipeline.h>
-#include <anki/gr/gl/PipelineImpl.h>
-#include <anki/gr/GrManager.h>
-#include <anki/gr/gl/CommandBufferImpl.h>
-#include <anki/core/Trace.h>
-
-namespace anki
-{
-
-Pipeline::Pipeline(GrManager* manager, U64 hash, GrObjectCache* cache)
-	: GrObject(manager, CLASS_TYPE, hash, cache)
-{
-	ANKI_TRACE_INC_COUNTER(GR_PIPELINES_CREATED, 1);
-}
-
-Pipeline::~Pipeline()
-{
-}
-
-class CreatePipelineCommand final : public GlCommand
-{
-public:
-	PipelinePtr m_ppline;
-	PipelineInitInfo m_init;
-
-	CreatePipelineCommand(Pipeline* ppline, const PipelineInitInfo& init)
-		: m_ppline(ppline)
-		, m_init(init)
-	{
-	}
-
-	Error operator()(GlState&)
-	{
-		PipelineImpl& impl = *m_ppline->m_impl;
-
-		Error err = impl.init(m_init);
-
-		GlObject::State oldState = impl.setStateAtomically(err ? GlObject::State::ERROR : GlObject::State::CREATED);
-		ANKI_ASSERT(oldState == GlObject::State::TO_BE_CREATED);
-		(void)oldState;
-
-		return err;
-	}
-};
-
-void Pipeline::init(const PipelineInitInfo& init)
-{
-	m_impl.reset(getAllocator().newInstance<PipelineImpl>(&getManager()));
-
-	CommandBufferInitInfo inf;
-	CommandBufferPtr cmdb = getManager().newInstance<CommandBuffer>(inf);
-
-	cmdb->m_impl->pushBackNewCommand<CreatePipelineCommand>(this, init);
-	cmdb->flush();
-}
-
-} // end namespace anki

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

@@ -1,624 +0,0 @@
-// 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/PipelineImpl.h>
-#include <anki/gr/gl/ShaderImpl.h>
-#include <anki/gr/gl/GlState.h>
-#include <anki/gr/common/Misc.h>
-#include <anki/util/Logger.h>
-#include <anki/util/Hash.h>
-
-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)
-{
-	ANKI_ASSERT(n == 1);
-	ANKI_ASSERT(names);
-	glDeleteProgram(*names);
-}
-
-PipelineImpl::~PipelineImpl()
-{
-	destroyDeferred(deletePrograms);
-}
-
-Error PipelineImpl::init(const PipelineInitInfo& init)
-{
-	m_in = init;
-
-	ANKI_CHECK(createGlPipeline());
-	if(!m_compute)
-	{
-		initVertexState();
-		initInputAssemblerState();
-		initTessellationState();
-		initRasterizerState();
-		initDepthStencilState();
-		initColorState();
-	}
-
-	return ErrorCode::NONE;
-}
-
-Error PipelineImpl::createGlPipeline()
-{
-	Error err = ErrorCode::NONE;
-
-	// Do checks
-	U mask = 0;
-	U count = 6;
-	while(count-- != 0)
-	{
-		const ShaderPtr& shader = m_in.m_shaders[count];
-		if(shader.isCreated())
-		{
-			ANKI_ASSERT(count == enumToType(shader->m_impl->m_type));
-			mask |= 1 << count;
-		}
-	}
-
-	if(mask & (1 << 5))
-	{
-		// Is compute
-
-		m_compute = true;
-
-		ANKI_ASSERT((mask & (1 << 5)) == (1 << 5) && "Compute should be alone in the pipeline");
-	}
-	else
-	{
-		m_compute = false;
-
-		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
-	m_glName = glCreateProgram();
-	ANKI_ASSERT(m_glName != 0);
-
-	for(U i = 0; i < m_in.m_shaders.getSize(); i++)
-	{
-		ShaderPtr& shader = m_in.m_shaders[i];
-
-		if(shader.isCreated())
-		{
-			glAttachShader(m_glName, shader->m_impl->getGlName());
-
-			if(i == U(ShaderType::TESSELLATION_CONTROL) || i == U(ShaderType::TESSELLATION_EVALUATION))
-			{
-				m_tessellation = true;
-			}
-		}
-	}
-
-	// Validate and check error
-	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("Ppline error log follows (vs:%u, fs:%u):\n%s",
-			m_in.m_shaders[ShaderType::VERTEX].isCreated() ? m_in.m_shaders[ShaderType::VERTEX]->m_impl->getGlName()
-														   : -1,
-			m_in.m_shaders[ShaderType::FRAGMENT].isCreated() ? m_in.m_shaders[ShaderType::FRAGMENT]->m_impl->getGlName()
-															 : -1,
-			&infoLogTxt[0]);
-		err = ErrorCode::USER_DATA;
-	}
-
-	return err;
-}
-
-void PipelineImpl::bind(GlState& state)
-{
-	glUseProgram(m_glName);
-
-	if(m_compute)
-	{
-		return;
-	}
-
-	// Set state
-	setVertexState(state);
-	setInputAssemblerState(state);
-	setTessellationState(state);
-	setViewportState(state);
-	setRasterizerState(state);
-	setDepthStencilState(state);
-	setColorState(state);
-}
-
-void PipelineImpl::initVertexState()
-{
-	for(U i = 0; i < m_in.m_vertex.m_attributeCount; ++i)
-	{
-		const VertexAttributeBinding& binding = m_in.m_vertex.m_attributes[i];
-		Attribute& cache = m_cache.m_attribs[i];
-
-		// Component count
-		if(binding.m_format == PixelFormat(ComponentFormat::R32, TransformFormat::FLOAT))
-		{
-			cache.m_compCount = 1;
-			cache.m_type = GL_FLOAT;
-			cache.m_normalized = false;
-		}
-		else if(binding.m_format == PixelFormat(ComponentFormat::R32G32, TransformFormat::FLOAT))
-		{
-			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::R32G32B32A32, TransformFormat::FLOAT))
-		{
-			cache.m_compCount = 4;
-			cache.m_type = GL_FLOAT;
-			cache.m_normalized = false;
-		}
-		else if(binding.m_format == PixelFormat(ComponentFormat::R16G16, TransformFormat::FLOAT))
-		{
-			cache.m_compCount = 2;
-			cache.m_type = GL_HALF_FLOAT;
-			cache.m_normalized = false;
-		}
-		else if(binding.m_format == PixelFormat(ComponentFormat::R16G16, TransformFormat::UNORM))
-		{
-			cache.m_compCount = 2;
-			cache.m_type = GL_UNSIGNED_SHORT;
-			cache.m_normalized = true;
-		}
-		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 if(binding.m_format == PixelFormat(ComponentFormat::R8G8B8A8, TransformFormat::UNORM))
-		{
-			cache.m_compCount = 4;
-			cache.m_type = GL_UNSIGNED_BYTE;
-			cache.m_normalized = true;
-		}
-		else if(binding.m_format == PixelFormat(ComponentFormat::R8G8B8, TransformFormat::UNORM))
-		{
-			cache.m_compCount = 3;
-			cache.m_type = GL_UNSIGNED_BYTE;
-			cache.m_normalized = true;
-		}
-		else
-		{
-			ANKI_ASSERT(0 && "TODO");
-		}
-	}
-
-	m_hashes.m_vertex = computeHash(&m_in.m_vertex, sizeof(m_in.m_vertex));
-}
-
-void PipelineImpl::initInputAssemblerState()
-{
-	switch(m_in.m_inputAssembler.m_topology)
-	{
-	case PrimitiveTopology::POINTS:
-		m_cache.m_topology = GL_POINTS;
-		break;
-	case PrimitiveTopology::LINES:
-		m_cache.m_topology = GL_LINES;
-		break;
-	case PrimitiveTopology::LINE_STIP:
-		m_cache.m_topology = GL_LINE_STRIP;
-		break;
-	case PrimitiveTopology::TRIANGLES:
-		m_cache.m_topology = GL_TRIANGLES;
-		break;
-	case PrimitiveTopology::TRIANGLE_STRIP:
-		m_cache.m_topology = GL_TRIANGLE_STRIP;
-		break;
-	case PrimitiveTopology::PATCHES:
-		m_cache.m_topology = GL_PATCHES;
-		break;
-	default:
-		ANKI_ASSERT(0);
-	}
-
-	m_hashes.m_inputAssembler = computeHash(&m_in.m_inputAssembler, sizeof(m_in.m_inputAssembler));
-}
-
-void PipelineImpl::initTessellationState()
-{
-	m_hashes.m_tessellation = computeHash(&m_in.m_tessellation, sizeof(m_in.m_tessellation));
-}
-
-void PipelineImpl::initRasterizerState()
-{
-	switch(m_in.m_rasterizer.m_fillMode)
-	{
-	case FillMode::POINTS:
-		m_cache.m_fillMode = GL_POINT;
-		break;
-	case FillMode::WIREFRAME:
-		m_cache.m_fillMode = GL_LINE;
-		break;
-	case FillMode::SOLID:
-		m_cache.m_fillMode = GL_FILL;
-		break;
-	default:
-		ANKI_ASSERT(0);
-	}
-
-	switch(m_in.m_rasterizer.m_cullMode)
-	{
-	case FaceSelectionMask::FRONT:
-		m_cache.m_cullMode = GL_FRONT;
-		break;
-	case FaceSelectionMask::BACK:
-		m_cache.m_cullMode = GL_BACK;
-		break;
-	case FaceSelectionMask::FRONT_AND_BACK:
-		m_cache.m_cullMode = GL_FRONT_AND_BACK;
-		break;
-	default:
-		ANKI_ASSERT(0);
-	}
-
-	m_hashes.m_rasterizer = computeHash(&m_in.m_rasterizer, sizeof(m_in.m_rasterizer));
-}
-
-void PipelineImpl::initDepthStencilState()
-{
-	const auto& ds = m_in.m_depthStencil;
-
-	// Depth
-	m_cache.m_depthCompareFunction = convertCompareOperation(ds.m_depthCompareFunction);
-
-	// Stencil
-	m_cache.m_stencilFailOp[0] = convertStencilOperation(ds.m_stencilFront.m_stencilFailOperation);
-	m_cache.m_stencilPassDepthFailOp[0] = convertStencilOperation(ds.m_stencilFront.m_stencilPassDepthFailOperation);
-	m_cache.m_stencilPassDepthPassOp[0] = convertStencilOperation(ds.m_stencilFront.m_stencilPassDepthPassOperation);
-	m_cache.m_stencilCompareFunc[0] = convertCompareOperation(ds.m_stencilFront.m_compareFunction);
-
-	m_cache.m_stencilFailOp[1] = convertStencilOperation(ds.m_stencilBack.m_stencilFailOperation);
-	m_cache.m_stencilPassDepthFailOp[1] = convertStencilOperation(ds.m_stencilBack.m_stencilPassDepthFailOperation);
-	m_cache.m_stencilPassDepthPassOp[1] = convertStencilOperation(ds.m_stencilBack.m_stencilPassDepthPassOperation);
-	m_cache.m_stencilCompareFunc[1] = convertCompareOperation(ds.m_stencilBack.m_compareFunction);
-
-	if(stencilTestDisabled(ds.m_stencilFront) && stencilTestDisabled(ds.m_stencilBack))
-	{
-		m_stencilTestEnabled = false;
-	}
-	else
-	{
-		m_stencilTestEnabled = true;
-	}
-
-	m_hashes.m_depthStencil = computeHash(&ds, sizeof(ds));
-}
-
-void PipelineImpl::initColorState()
-{
-	for(U i = 0; i < m_in.m_color.m_attachmentCount; ++i)
-	{
-		Attachment& out = m_cache.m_attachments[i];
-		const ColorAttachmentStateInfo& in = m_in.m_color.m_attachments[i];
-
-		out.m_srcBlendMethod = convertBlendMethod(in.m_srcBlendMethod);
-		out.m_dstBlendMethod = convertBlendMethod(in.m_dstBlendMethod);
-
-		switch(in.m_blendFunction)
-		{
-		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) != 0;
-		out.m_channelWriteMask[1] = (in.m_channelWriteMask & ColorBit::GREEN) != 0;
-		out.m_channelWriteMask[2] = (in.m_channelWriteMask & ColorBit::BLUE) != 0;
-		out.m_channelWriteMask[3] = (in.m_channelWriteMask & ColorBit::ALPHA) != 0;
-
-		if(!(out.m_srcBlendMethod == GL_ONE && out.m_dstBlendMethod == GL_ZERO))
-		{
-			m_blendEnabled = true;
-		}
-	}
-
-	m_hashes.m_color = computeHash(&m_in.m_color, sizeof(m_in.m_color));
-}
-
-void PipelineImpl::setVertexState(GlState& state) const
-{
-	if(state.m_stateHashes.m_vertex == m_hashes.m_vertex)
-	{
-		return;
-	}
-
-	state.m_stateHashes.m_vertex = m_hashes.m_vertex;
-
-	for(U i = 0; i < m_in.m_vertex.m_attributeCount; ++i)
-	{
-		const Attribute& attrib = m_cache.m_attribs[i];
-		ANKI_ASSERT(attrib.m_type);
-
-		glVertexAttribFormat(
-			i, attrib.m_compCount, attrib.m_type, attrib.m_normalized, m_in.m_vertex.m_attributes[i].m_offset);
-
-		glVertexAttribBinding(i, m_in.m_vertex.m_attributes[i].m_binding);
-	}
-
-	for(U i = 0; i < m_in.m_vertex.m_bindingCount; ++i)
-	{
-		ANKI_ASSERT(m_in.m_vertex.m_bindings[i].m_stride > 0);
-		state.m_vertexBindingStrides[i] = m_in.m_vertex.m_bindings[i].m_stride;
-	}
-
-	if(m_in.m_vertex.m_bindingCount)
-	{
-		state.m_vertBindingCount = m_in.m_vertex.m_bindingCount;
-		state.m_vertBindingsDirty = true;
-	}
-}
-
-void PipelineImpl::setInputAssemblerState(GlState& state) const
-{
-	if(state.m_stateHashes.m_inputAssembler == m_hashes.m_inputAssembler)
-	{
-		return;
-	}
-
-	state.m_stateHashes.m_inputAssembler = m_hashes.m_inputAssembler;
-
-	state.m_topology = m_cache.m_topology;
-
-	if(m_in.m_inputAssembler.m_primitiveRestartEnabled)
-	{
-		glEnable(GL_PRIMITIVE_RESTART);
-	}
-	else
-	{
-		glDisable(GL_PRIMITIVE_RESTART);
-	}
-}
-
-void PipelineImpl::setTessellationState(GlState& state) const
-{
-	if(!m_tessellation || state.m_stateHashes.m_tessellation == m_hashes.m_tessellation)
-	{
-		return;
-	}
-
-	state.m_stateHashes.m_tessellation = m_hashes.m_tessellation;
-
-	glPatchParameteri(GL_PATCH_VERTICES, m_in.m_tessellation.m_patchControlPointCount);
-}
-
-void PipelineImpl::setRasterizerState(GlState& state) const
-{
-	if(state.m_stateHashes.m_rasterizer == m_hashes.m_rasterizer)
-	{
-		return;
-	}
-
-	state.m_stateHashes.m_rasterizer = m_hashes.m_rasterizer;
-
-	glPolygonMode(GL_FRONT_AND_BACK, m_cache.m_fillMode);
-	glCullFace(m_cache.m_cullMode);
-	glEnable(GL_CULL_FACE);
-}
-
-void PipelineImpl::setDepthStencilState(GlState& state) const
-{
-	if(state.m_stateHashes.m_depthStencil == m_hashes.m_depthStencil)
-	{
-		return;
-	}
-
-	state.m_stateHashes.m_depthStencil = m_hashes.m_depthStencil;
-
-	// Depth
-	glDepthMask(m_in.m_depthStencil.m_depthWriteEnabled);
-	state.m_depthWriteMask = m_in.m_depthStencil.m_depthWriteEnabled;
-
-	if(m_cache.m_depthCompareFunction == GL_ALWAYS && !m_in.m_depthStencil.m_depthWriteEnabled)
-	{
-		glDisable(GL_DEPTH_TEST);
-	}
-	else
-	{
-		glEnable(GL_DEPTH_TEST);
-	}
-
-	glDepthFunc(m_cache.m_depthCompareFunction);
-
-	// Stencil
-	if(m_stencilTestEnabled)
-	{
-		glEnable(GL_STENCIL_TEST);
-	}
-	else
-	{
-		glDisable(GL_STENCIL_TEST);
-	}
-
-	glStencilOpSeparate(
-		GL_FRONT, m_cache.m_stencilFailOp[0], m_cache.m_stencilPassDepthFailOp[0], m_cache.m_stencilPassDepthPassOp[0]);
-
-	glStencilOpSeparate(
-		GL_BACK, m_cache.m_stencilFailOp[1], m_cache.m_stencilPassDepthFailOp[1], m_cache.m_stencilPassDepthPassOp[1]);
-
-	if(state.m_stencilCompareFunc[0] != m_cache.m_stencilCompareFunc[0])
-	{
-		state.m_stencilCompareFunc[0] = m_cache.m_stencilCompareFunc[0];
-		state.m_glStencilFuncSeparateDirtyMask |= 1 << 0;
-	}
-
-	if(state.m_stencilCompareFunc[1] != m_cache.m_stencilCompareFunc[1])
-	{
-		state.m_stencilCompareFunc[1] = m_cache.m_stencilCompareFunc[1];
-		state.m_glStencilFuncSeparateDirtyMask |= 1 << 1;
-	}
-}
-
-void PipelineImpl::setColorState(GlState& state) const
-{
-	if(state.m_stateHashes.m_color == m_hashes.m_color)
-	{
-		return;
-	}
-
-	state.m_stateHashes.m_color = m_hashes.m_color;
-
-	if(m_blendEnabled)
-	{
-		glEnable(GL_BLEND);
-
-		for(U i = 0; i < m_in.m_color.m_attachmentCount; ++i)
-		{
-			const Attachment& att = m_cache.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]);
-
-			state.m_colorWriteMasks[i][0] = att.m_channelWriteMask[0];
-			state.m_colorWriteMasks[i][1] = att.m_channelWriteMask[1];
-			state.m_colorWriteMasks[i][2] = att.m_channelWriteMask[2];
-			state.m_colorWriteMasks[i][3] = att.m_channelWriteMask[3];
-		}
-	}
-	else
-	{
-		glDisable(GL_BLEND);
-
-		for(U i = 0; i < m_in.m_color.m_attachmentCount; ++i)
-		{
-			const Attachment& att = m_cache.m_attachments[i];
-
-			glColorMaski(i,
-				att.m_channelWriteMask[0],
-				att.m_channelWriteMask[1],
-				att.m_channelWriteMask[2],
-				att.m_channelWriteMask[3]);
-
-			state.m_colorWriteMasks[i][0] = att.m_channelWriteMask[0];
-			state.m_colorWriteMasks[i][1] = att.m_channelWriteMask[1];
-			state.m_colorWriteMasks[i][2] = att.m_channelWriteMask[2];
-			state.m_colorWriteMasks[i][3] = att.m_channelWriteMask[3];
-		}
-	}
-}
-
-} // end namespace anki

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

@@ -1,112 +0,0 @@
-// 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>
-#include <anki/gr/Pipeline.h>
-
-namespace anki
-{
-
-/// @addtogroup opengl
-/// @{
-
-/// Program pipeline
-class PipelineImpl : public GlObject
-{
-public:
-	PipelineImpl(GrManager* manager)
-		: GlObject(manager)
-	{
-	}
-
-	~PipelineImpl();
-
-	ANKI_USE_RESULT Error init(const PipelineInitInfo& init);
-
-	/// Bind the pipeline to the state
-	void bind(GlState& state);
-
-private:
-	class Attribute
-	{
-	public:
-		GLenum m_type = 0;
-		U8 m_compCount = 0;
-		Bool8 m_normalized = false;
-	};
-
-	class Attachment
-	{
-	public:
-		GLenum m_srcBlendMethod = GL_ONE;
-		GLenum m_dstBlendMethod = GL_ZERO;
-		GLenum m_blendFunction = GL_ADD;
-		Array<Bool8, 4> m_channelWriteMask;
-	};
-
-	Bool8 m_compute = false; ///< Is compute
-	Bool8 m_tessellation = false;
-	Bool8 m_blendEnabled = false;
-	Bool8 m_stencilTestEnabled = false;
-
-	/// Input values.
-	PipelineInitInfo m_in;
-
-	/// Cached values.
-	class
-	{
-	public:
-		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;
-
-		Array<GLenum, 2> m_stencilFailOp = {{0, 0}};
-		Array<GLenum, 2> m_stencilPassDepthFailOp = {{0, 0}};
-		Array<GLenum, 2> m_stencilPassDepthPassOp = {{0, 0}};
-		Array<GLenum, 2> m_stencilCompareFunc = {{0, 0}};
-	} m_cache;
-
-	/// State hashes.
-	class
-	{
-	public:
-		U64 m_vertex = 0;
-		U64 m_inputAssembler = 0;
-		U64 m_tessellation = 0;
-		U64 m_viewport = 0;
-		U64 m_rasterizer = 0;
-		U64 m_depthStencil = 0;
-		U64 m_color = 0;
-	} m_hashes;
-
-	/// Attach all the programs
-	ANKI_USE_RESULT Error createGlPipeline();
-
-	void initVertexState();
-	void initInputAssemblerState();
-	void initTessellationState();
-	void initRasterizerState();
-	void initDepthStencilState();
-	void initColorState();
-
-	void setVertexState(GlState& state) const;
-	void setInputAssemblerState(GlState& state) const;
-	void setTessellationState(GlState& state) const;
-	void setViewportState(GlState& state) const
-	{
-	}
-	void setRasterizerState(GlState& state) const;
-	void setDepthStencilState(GlState& state) const;
-	void setColorState(GlState& state) const;
-};
-/// @}
-
-} // end namespace anki

+ 0 - 64
src/anki/gr/gl/ResourceGroup.cpp

@@ -1,64 +0,0 @@
-// 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/ResourceGroup.h>
-#include <anki/gr/gl/ResourceGroupImpl.h>
-#include <anki/gr/GrManager.h>
-#include <anki/gr/gl/CommandBufferImpl.h>
-#include <anki/gr/Texture.h>
-#include <anki/gr/Sampler.h>
-#include <anki/gr/Buffer.h>
-
-namespace anki
-{
-
-ResourceGroup::ResourceGroup(GrManager* manager, U64 hash, GrObjectCache* cache)
-	: GrObject(manager, CLASS_TYPE, hash, cache)
-{
-}
-
-ResourceGroup::~ResourceGroup()
-{
-}
-
-class RcgCreateCommand final : public GlCommand
-{
-public:
-	ResourceGroupPtr m_ptr;
-	ResourceGroupInitInfo m_init;
-
-	RcgCreateCommand(ResourceGroup* ptr, const ResourceGroupInitInfo& init)
-		: m_ptr(ptr)
-		, m_init(init)
-	{
-	}
-
-	Error operator()(GlState&)
-	{
-		ResourceGroupImpl& impl = *m_ptr->m_impl;
-
-		impl.init(m_init);
-
-		GlObject::State oldState = impl.setStateAtomically(GlObject::State::CREATED);
-
-		(void)oldState;
-		ANKI_ASSERT(oldState == GlObject::State::TO_BE_CREATED);
-
-		return ErrorCode::NONE;
-	}
-};
-
-void ResourceGroup::init(const ResourceGroupInitInfo& init)
-{
-	// NOTE: Create asynchronously because the initialization touches GL names
-	m_impl.reset(getAllocator().newInstance<ResourceGroupImpl>(&getManager()));
-
-	CommandBufferPtr cmdb = getManager().newInstance<CommandBuffer>(CommandBufferInitInfo());
-
-	cmdb->m_impl->pushBackNewCommand<RcgCreateCommand>(this, init);
-	cmdb->flush();
-}
-
-} // end namespace anki

+ 0 - 384
src/anki/gr/gl/ResourceGroupImpl.cpp

@@ -1,384 +0,0 @@
-// 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/ResourceGroupImpl.h>
-#include <anki/gr/gl/TextureImpl.h>
-#include <anki/gr/Texture.h>
-#include <anki/gr/gl/SamplerImpl.h>
-#include <anki/gr/Sampler.h>
-#include <anki/gr/gl/BufferImpl.h>
-#include <anki/gr/Buffer.h>
-#include <anki/gr/gl/GlState.h>
-#include <anki/gr/gl/GrManagerImpl.h>
-#include <anki/gr/GrManager.h>
-#include <anki/gr/gl/RenderingThread.h>
-#include <anki/gr/gl/CommandBufferImpl.h>
-#include <anki/gr/gl/TransientMemoryManager.h>
-
-namespace anki
-{
-
-template<typename InBindings, typename OutBindings>
-void ResourceGroupImpl::initBuffers(const InBindings& in, OutBindings& out, U8& count, U& resourcesCount, U& transCount)
-{
-	count = 0;
-
-	for(U i = 0; i < in.getSize(); ++i)
-	{
-		const BufferBinding& binding = in[i];
-
-		if(binding.m_buffer.isCreated())
-		{
-			ANKI_ASSERT(binding.m_uploadedMemory == false);
-
-			const BufferImpl& buff = *binding.m_buffer->m_impl;
-			InternalBufferBinding& outBinding = out[count];
-
-			outBinding.m_name = buff.getGlName();
-			outBinding.m_offset = binding.m_offset;
-			outBinding.m_range = (binding.m_range != 0) ? binding.m_range : (buff.m_size - binding.m_offset);
-
-			ANKI_ASSERT(outBinding.m_offset + outBinding.m_range <= buff.m_size);
-			ANKI_ASSERT(outBinding.m_range > 0);
-
-			++resourcesCount;
-			count = i + 1;
-		}
-		else if(binding.m_uploadedMemory)
-		{
-			InternalBufferBinding& outBinding = out[count];
-			outBinding.m_name = MAX_U32;
-			++transCount;
-			count = i + 1;
-		}
-	}
-}
-
-void ResourceGroupImpl::init(const ResourceGroupInitInfo& init)
-{
-	U resourcesCount = 0;
-	U transCount = 0;
-
-	// Init textures & samplers
-	m_textureNamesCount = 0;
-	m_allSamplersZero = true;
-	for(U i = 0; i < init.m_textures.getSize(); ++i)
-	{
-		if(init.m_textures[i].m_texture.isCreated())
-		{
-			m_textureNames[i] = init.m_textures[i].m_texture->m_impl->getGlName();
-			m_textureNamesCount = i + 1;
-			++resourcesCount;
-		}
-		else
-		{
-			m_textureNames[i] = 0;
-		}
-
-		if(init.m_textures[i].m_sampler.isCreated())
-		{
-			m_samplerNames[i] = init.m_textures[i].m_sampler->m_impl->getGlName();
-			m_allSamplersZero = false;
-			++resourcesCount;
-		}
-		else
-		{
-			m_samplerNames[i] = 0;
-		}
-	}
-
-	// Init shader buffers
-	initBuffers(init.m_uniformBuffers, m_ubos, m_ubosCount, resourcesCount, transCount);
-	initBuffers(init.m_storageBuffers, m_ssbos, m_ssbosCount, resourcesCount, transCount);
-
-	// Init images
-	for(U i = 0; i < MAX_IMAGE_BINDINGS; ++i)
-	{
-		const auto& in = init.m_images[i];
-		if(in.m_texture)
-		{
-			TextureImpl& impl = *in.m_texture->m_impl;
-			impl.checkSurface(TextureSurfaceInfo(in.m_level, 0, 0, 0));
-
-			ImageBinding& out = m_images[i];
-
-			out.m_name = in.m_texture->m_impl->getGlName();
-			out.m_level = in.m_level;
-			out.m_format = impl.m_internalFormat;
-
-			++m_imageCount;
-			++resourcesCount;
-		}
-	}
-
-	// Init vert buffers
-	m_vertBindingsCount = 0;
-	for(U i = 0; i < init.m_vertexBuffers.getSize(); ++i)
-	{
-		const BufferBinding& binding = init.m_vertexBuffers[i];
-		if(binding.m_buffer.isCreated())
-		{
-			ANKI_ASSERT(!binding.m_uploadedMemory);
-
-			m_vertBuffNames[i] = binding.m_buffer->m_impl->getGlName();
-			m_vertBuffOffsets[i] = binding.m_offset;
-
-			++m_vertBindingsCount;
-			++resourcesCount;
-		}
-		else if(binding.m_uploadedMemory)
-		{
-			++transCount;
-
-			m_vertBuffNames[i] = 0;
-			m_vertBuffOffsets[i] = MAX_U32;
-
-			++m_vertBindingsCount;
-			m_hasTransientVertexBuff = true;
-		}
-		else
-		{
-			m_vertBuffNames[i] = 0;
-			m_vertBuffOffsets[i] = 0;
-		}
-	}
-
-	// Init index buffer
-	if(init.m_indexBuffer.m_buffer.isCreated())
-	{
-		const BufferImpl& buff = *init.m_indexBuffer.m_buffer->m_impl;
-
-		ANKI_ASSERT(init.m_indexSize == 2 || init.m_indexSize == 4);
-
-		m_indexBuffName = buff.getGlName();
-		m_indexSize = init.m_indexSize;
-		++resourcesCount;
-	}
-
-	ANKI_ASSERT((resourcesCount > 0 || transCount > 0) && "Resource group empty");
-
-	// Hold references
-	initResourceReferences(init, resourcesCount);
-}
-
-void ResourceGroupImpl::initResourceReferences(const ResourceGroupInitInfo& init, U refCount)
-{
-	m_refs.create(getAllocator(), refCount);
-
-	U count = 0;
-
-	for(U i = 0; i < init.m_textures.getSize(); ++i)
-	{
-		if(init.m_textures[i].m_texture.isCreated())
-		{
-			m_refs[count++] = init.m_textures[i].m_texture;
-		}
-
-		if(init.m_textures[i].m_sampler.isCreated())
-		{
-			m_refs[count++] = init.m_textures[i].m_sampler;
-		}
-	}
-
-	for(U i = 0; i < init.m_uniformBuffers.getSize(); ++i)
-	{
-		const BufferBinding& binding = init.m_uniformBuffers[i];
-		if(binding.m_buffer.isCreated())
-		{
-			m_refs[count++] = binding.m_buffer;
-		}
-	}
-
-	for(U i = 0; i < init.m_storageBuffers.getSize(); ++i)
-	{
-		const BufferBinding& binding = init.m_storageBuffers[i];
-		if(binding.m_buffer.isCreated())
-		{
-			m_refs[count++] = binding.m_buffer;
-		}
-	}
-
-	for(U i = 0; i < MAX_IMAGE_BINDINGS; ++i)
-	{
-		const auto& binding = init.m_images[i];
-		if(binding.m_texture)
-		{
-			m_refs[count++] = binding.m_texture;
-		}
-	}
-
-	for(U i = 0; i < init.m_vertexBuffers.getSize(); ++i)
-	{
-		const BufferBinding& binding = init.m_vertexBuffers[i];
-		if(binding.m_buffer.isCreated())
-		{
-			m_refs[count++] = binding.m_buffer;
-		}
-	}
-
-	if(init.m_indexBuffer.m_buffer.isCreated())
-	{
-		m_refs[count++] = init.m_indexBuffer.m_buffer;
-	}
-
-	ANKI_ASSERT(refCount == count);
-}
-
-void ResourceGroupImpl::bind(U slot, const TransientMemoryInfo& transientInfo, GlState& state)
-{
-	ANKI_ASSERT(slot < MAX_BOUND_RESOURCE_GROUPS);
-
-	// Bind textures
-	if(m_textureNamesCount)
-	{
-		glBindTextures(MAX_TEXTURE_BINDINGS * slot, m_textureNamesCount, &m_textureNames[0]);
-
-		if(m_allSamplersZero)
-		{
-			glBindSamplers(MAX_TEXTURE_BINDINGS * slot, m_textureNamesCount, nullptr);
-		}
-		else
-		{
-			glBindSamplers(MAX_TEXTURE_BINDINGS * slot, m_textureNamesCount, &m_samplerNames[0]);
-		}
-	}
-
-	// Uniform buffers
-	for(U i = 0; i < m_ubosCount; ++i)
-	{
-		const auto& binding = m_ubos[i];
-		if(binding.m_name == MAX_U32)
-		{
-			// Transient
-			TransientMemoryToken token = transientInfo.m_uniformBuffers[i];
-			ANKI_ASSERT(token.m_range != 0);
-
-			if(!token.isUnused())
-			{
-				glBindBufferRange(GL_UNIFORM_BUFFER,
-					MAX_UNIFORM_BUFFER_BINDINGS * slot + i,
-					getManager().getImplementation().getTransientMemoryManager().getGlName(token),
-					token.m_offset,
-					token.m_range);
-			}
-			else
-			{
-				// It's unused
-			}
-		}
-		else if(binding.m_name != 0)
-		{
-			// Static
-			glBindBufferRange(GL_UNIFORM_BUFFER,
-				MAX_UNIFORM_BUFFER_BINDINGS * slot + i,
-				binding.m_name,
-				binding.m_offset,
-				binding.m_range);
-		}
-	}
-
-	// Storage buffers
-	for(U i = 0; i < m_ssbosCount; ++i)
-	{
-		const auto& binding = m_ssbos[i];
-		if(binding.m_name == MAX_U32)
-		{
-			// Transient
-			TransientMemoryToken token = transientInfo.m_storageBuffers[i];
-			ANKI_ASSERT(token.m_range != 0);
-
-			if(!token.isUnused())
-			{
-				glBindBufferRange(GL_SHADER_STORAGE_BUFFER,
-					MAX_STORAGE_BUFFER_BINDINGS * slot + i,
-					getManager().getImplementation().getTransientMemoryManager().getGlName(token),
-					token.m_offset,
-					token.m_range);
-			}
-			else
-			{
-				// It's unused
-			}
-		}
-		else if(binding.m_name != 0)
-		{
-			// Static
-			glBindBufferRange(GL_SHADER_STORAGE_BUFFER,
-				MAX_STORAGE_BUFFER_BINDINGS * slot + i,
-				binding.m_name,
-				binding.m_offset,
-				binding.m_range);
-		}
-	}
-
-	// Images
-	for(U i = 0; i < m_imageCount; ++i)
-	{
-		const ImageBinding& binding = m_images[i];
-		if(binding.m_name)
-		{
-			glBindImageTexture(MAX_IMAGE_BINDINGS * slot + i,
-				binding.m_name,
-				binding.m_level,
-				GL_TRUE,
-				0,
-				GL_READ_WRITE,
-				binding.m_format);
-		}
-	}
-
-	// Vertex buffers
-	if(m_vertBindingsCount)
-	{
-		ANKI_ASSERT(slot == 0 && "Only slot 0 can have vertex buffers");
-
-		if(!m_hasTransientVertexBuff)
-		{
-			memcpy(
-				&state.m_vertBuffOffsets[0], &m_vertBuffOffsets[0], sizeof(m_vertBuffOffsets[0]) * m_vertBindingsCount);
-
-			memcpy(&state.m_vertBuffNames[0], &m_vertBuffNames[0], sizeof(m_vertBuffNames[0]) * m_vertBindingsCount);
-		}
-		else
-		{
-			Array<GLintptr, MAX_VERTEX_ATTRIBUTES> offsets = m_vertBuffOffsets;
-			Array<GLuint, MAX_VERTEX_ATTRIBUTES> names = m_vertBuffNames;
-			const TransientMemoryManager& transManager = getManager().getImplementation().getTransientMemoryManager();
-
-			for(U i = 0; i < MAX_VERTEX_ATTRIBUTES; ++i)
-			{
-				if(offsets[i] == MAX_U32)
-				{
-					// It's dynamic
-					ANKI_ASSERT(transientInfo.m_vertexBuffers[i].m_range != 0);
-					offsets[i] = transientInfo.m_vertexBuffers[i].m_offset;
-					names[i] = transManager.getGlName(transientInfo.m_vertexBuffers[i]);
-				}
-				else
-				{
-					ANKI_ASSERT(transientInfo.m_vertexBuffers[i].m_range == 0);
-				}
-			}
-
-			// Bind to state
-			memcpy(&state.m_vertBuffOffsets[0], &offsets[0], sizeof(offsets[0]) * m_vertBindingsCount);
-
-			memcpy(&state.m_vertBuffNames[0], &names[0], sizeof(names[0]) * m_vertBindingsCount);
-		}
-
-		state.m_vertBindingCount = m_vertBindingsCount;
-		state.m_vertBindingsDirty = true;
-	}
-
-	// Index buffer
-	if(m_indexSize > 0)
-	{
-		ANKI_ASSERT(slot == 0 && "Only slot 0 can have index buffers");
-		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBuffName);
-		state.m_indexSize = m_indexSize;
-	}
-}
-
-} // end namespace anki

+ 0 - 86
src/anki/gr/gl/ResourceGroupImpl.h

@@ -1,86 +0,0 @@
-// 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>
-#include <anki/gr/ResourceGroup.h>
-#include <anki/util/DynamicArray.h>
-
-namespace anki
-{
-
-/// @addtogroup opengl
-/// @{
-
-/// Resource group implementation.
-class ResourceGroupImpl : public GlObject
-{
-public:
-	ResourceGroupImpl(GrManager* manager)
-		: GlObject(manager)
-	{
-	}
-
-	~ResourceGroupImpl()
-	{
-		m_refs.destroy(getAllocator());
-	}
-
-	void init(const ResourceGroupInitInfo& init);
-
-	/// Set state.
-	void bind(U slot, const TransientMemoryInfo& transientInfo, GlState& state);
-
-private:
-	class InternalBufferBinding
-	{
-	public:
-		GLuint m_name = 0; ///< If it's MAX_U32 then it's transient
-		U32 m_offset = 0;
-		U32 m_range = 0;
-	};
-
-	class ImageBinding
-	{
-	public:
-		GLuint m_name = 0;
-		U16 m_level = 0;
-		GLenum m_format = GL_NONE;
-	};
-
-	Array<GLuint, MAX_TEXTURE_BINDINGS> m_textureNames;
-	Array<GLuint, MAX_TEXTURE_BINDINGS> m_samplerNames;
-	U8 m_textureNamesCount = 0;
-	Bool8 m_allSamplersZero = false;
-
-	Array<InternalBufferBinding, MAX_UNIFORM_BUFFER_BINDINGS> m_ubos;
-	U8 m_ubosCount = 0;
-
-	Array<InternalBufferBinding, MAX_STORAGE_BUFFER_BINDINGS> m_ssbos;
-	U8 m_ssbosCount = 0;
-
-	Array<ImageBinding, MAX_IMAGE_BINDINGS> m_images;
-	U8 m_imageCount = 0;
-
-	Array<GLuint, MAX_VERTEX_ATTRIBUTES> m_vertBuffNames;
-	Array<GLintptr, MAX_VERTEX_ATTRIBUTES> m_vertBuffOffsets;
-	Bool8 m_hasTransientVertexBuff = false;
-	U8 m_vertBindingsCount = 0;
-
-	GLuint m_indexBuffName = 0;
-	U8 m_indexSize = 0;
-
-	/// Holds the references to the resources. Used to release the references gracefully.
-	DynamicArray<GrObjectPtr<GrObject>> m_refs;
-
-	template<typename InBindings, typename OutBindings>
-	void initBuffers(const InBindings& in, OutBindings& out, U8& count, U& resourcesCount, U& transCount);
-
-	void initResourceReferences(const ResourceGroupInitInfo& init, U count);
-};
-/// @}
-
-} // end namespace anki

+ 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()));
 
 	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

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

@@ -0,0 +1,78 @@
+// 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);
+
+	glAttachShader(m_glName, comp->m_impl->getGlName());
+
+	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

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

@@ -0,0 +1,615 @@
+// 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>
+#include <anki/gr/Framebuffer.h>
+#include <anki/gr/gl/FramebufferImpl.h>
+#include <anki/gr/Buffer.h>
+#include <anki/gr/gl/BufferImpl.h>
+#include <anki/gr/Sampler.h>
+#include <anki/gr/gl/SamplerImpl.h>
+#include <anki/gr/common/Misc.h>
+
+namespace anki
+{
+
+/// @addtogroup opengl
+/// @{
+
+/// Local state tracker. Used to avoid creating command buffer commands.
+class StateTracker
+{
+public:
+	/// If it's false then there might be unset state.
+	Bool8 m_mayContainUnsetState = true;
+
+#if ANKI_ASSERTIONS
+	Bool8 m_secondLevel = false;
+#endif
+
+	/// @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;
+
+	Bool setVertexAttribute(U32 location, U32 buffBinding, const PixelFormat& fmt, PtrSize relativeOffset)
+	{
+		VertexAttribute& attrib = m_attribs[location];
+		if(attrib.m_buffBinding != buffBinding || attrib.m_fmt != fmt || attrib.m_relativeOffset != relativeOffset)
+		{
+			attrib.m_buffBinding = buffBinding;
+			attrib.m_fmt = fmt;
+			attrib.m_relativeOffset = relativeOffset;
+			return true;
+		}
+
+		return false;
+	}
+
+	class VertexBuffer
+	{
+	public:
+		BufferImpl* m_buff = nullptr;
+		PtrSize m_offset = 0;
+		PtrSize m_stride = 0;
+		TransientMemoryToken m_token;
+	};
+
+	Array<VertexBuffer, MAX_VERTEX_ATTRIBUTES> m_vertBuffs;
+
+	Bool bindVertexBuffer(U32 binding, BufferPtr buff, PtrSize offset, PtrSize stride)
+	{
+		VertexBuffer& b = m_vertBuffs[binding];
+		b.m_buff = buff->m_impl.get();
+		b.m_offset = offset;
+		b.m_stride = stride;
+		b.m_token = {};
+		return true;
+	}
+
+	Bool bindVertexBuffer(U32 binding, const TransientMemoryToken& token, PtrSize stride)
+	{
+		VertexBuffer& b = m_vertBuffs[binding];
+		b.m_buff = nullptr;
+		b.m_offset = 0;
+		b.m_stride = stride;
+		b.m_token = token;
+		return true;
+	}
+
+	class Index
+	{
+	public:
+		BufferImpl* m_buff = nullptr;
+		PtrSize m_offset = MAX_PTR_SIZE;
+		GLenum m_indexType = 0;
+	} m_idx;
+
+	Bool bindIndexBuffer(BufferPtr buff, PtrSize offset, IndexType type)
+	{
+		m_idx.m_buff = buff->m_impl.get();
+		m_idx.m_offset = offset;
+		m_idx.m_indexType = convertIndexType(type);
+		return true;
+	}
+	/// @}
+
+	/// @name input_assembly
+	/// @{
+	Bool8 m_primitiveRestart = 2;
+
+	Bool setPrimitiveRestart(Bool enable)
+	{
+		U enablei = enable ? 1 : 0;
+		if(enablei != m_primitiveRestart)
+		{
+			m_primitiveRestart = enablei;
+			return true;
+		}
+		return false;
+	}
+	/// @}
+
+	/// @name viewport_state
+	/// @{
+	Array<U16, 4> m_viewport = {{MAX_U16, MAX_U16, MAX_U16, MAX_U16}};
+
+	Bool setViewport(U16 minx, U16 miny, U16 maxx, U16 maxy)
+	{
+		ANKI_ASSERT(minx != MAX_U16 && miny != MAX_U16 && maxx != MAX_U16 && maxy != MAX_U16);
+		if(m_viewport[0] != minx || m_viewport[1] != miny || m_viewport[2] != maxx || m_viewport[3] != maxy)
+		{
+			m_viewport = {{minx, miny, maxx, maxy}};
+			return true;
+		}
+		return false;
+	}
+	/// @}
+
+	/// @name rasterizer
+	/// @{
+	FillMode m_fillMode = FillMode::COUNT;
+
+	Bool setFillMode(FillMode mode)
+	{
+		if(m_fillMode != mode)
+		{
+			m_fillMode = mode;
+			return true;
+		}
+		return false;
+	}
+
+	FaceSelectionMask m_cullMode = static_cast<FaceSelectionMask>(0);
+
+	Bool setCullMode(FaceSelectionMask mode)
+	{
+		if(m_cullMode != mode)
+		{
+			m_cullMode = mode;
+			return true;
+		}
+		return false;
+	}
+
+	F32 m_polyOffsetFactor = -1.0;
+	F32 m_polyOffsetUnits = -1.0;
+
+	Bool setPolygonOffset(F32 factor, F32 units)
+	{
+		if(factor != m_polyOffsetFactor || units != m_polyOffsetUnits)
+		{
+			m_polyOffsetFactor = factor;
+			m_polyOffsetUnits = units;
+			return true;
+		}
+		return false;
+	}
+	/// @}
+
+	/// @name depth_stencil
+	/// @{
+	Bool8 m_stencilTestEnabled = 2;
+
+	Bool maybeEnableStencilTest()
+	{
+		Bool enable = !stencilTestDisabled(
+			m_stencilFail[0], m_stencilPassDepthFail[0], m_stencilPassDepthPass[0], m_stencilCompare[0]);
+		enable = enable
+			|| !stencilTestDisabled(
+					 m_stencilFail[1], m_stencilPassDepthFail[1], m_stencilPassDepthPass[1], m_stencilCompare[1]);
+
+		if(enable != m_stencilTestEnabled)
+		{
+			m_stencilTestEnabled = enable;
+			return true;
+		}
+		return false;
+	}
+
+	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}};
+
+	Bool setStencilOperations(FaceSelectionMask face,
+		StencilOperation stencilFail,
+		StencilOperation stencilPassDepthFail,
+		StencilOperation stencilPassDepthPass)
+	{
+		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;
+		}
+
+		return changed;
+	}
+
+	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;
+		}
+	}
+
+	static const U32 DUMMY_STENCIL_MASK = 0x969696;
+
+	Array<U32, 2> m_stencilCompareMask = {{DUMMY_STENCIL_MASK, DUMMY_STENCIL_MASK}};
+
+	void setStencilCompareMask(FaceSelectionMask face, U32 mask)
+	{
+		ANKI_ASSERT(mask != DUMMY_STENCIL_MASK && "Oops");
+
+		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 = {{DUMMY_STENCIL_MASK, DUMMY_STENCIL_MASK}};
+
+	Bool setStencilWriteMask(FaceSelectionMask face, U32 mask)
+	{
+		ANKI_ASSERT(mask != DUMMY_STENCIL_MASK && "Oops");
+
+		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;
+		}
+
+		return changed;
+	}
+
+	Array<U32, 2> m_stencilRef = {{DUMMY_STENCIL_MASK, DUMMY_STENCIL_MASK}};
+
+	void setStencilReference(FaceSelectionMask face, U32 mask)
+	{
+		ANKI_ASSERT(mask != DUMMY_STENCIL_MASK && "Oops");
+
+		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_depthTestEnabled = 2; ///< 2 means don't know
+
+	Bool maybeEnableDepthTest()
+	{
+		ANKI_ASSERT(m_depthWrite <= 1 && m_depthOp != CompareOperation::COUNT);
+		Bool enable = m_depthWrite || m_depthOp != CompareOperation::ALWAYS;
+
+		if(enable != m_depthTestEnabled)
+		{
+			m_depthTestEnabled = enable;
+			return true;
+		}
+
+		return false;
+	}
+
+	Bool8 m_depthWrite = 2;
+
+	Bool setDepthWrite(Bool enable)
+	{
+		if(m_depthWrite != enable)
+		{
+			m_depthWrite = enable;
+			return true;
+		}
+		return false;
+	}
+
+	CompareOperation m_depthOp = CompareOperation::COUNT;
+
+	Bool setDepthCompareFunction(CompareOperation op)
+	{
+		if(op != m_depthOp)
+		{
+			m_depthOp = op;
+			return true;
+		}
+		return false;
+	}
+	/// @}
+
+	/// @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}};
+
+	Bool setColorChannelWriteMask(U32 attachment, ColorBit mask)
+	{
+		if(m_colorWriteMasks[attachment] != mask)
+		{
+			m_colorWriteMasks[attachment] = mask;
+			return true;
+		}
+		return false;
+	}
+
+	Bool8 m_enableBlend = 2;
+
+	Bool maybeEnableBlend()
+	{
+		Bool enable = false;
+
+		for(U i = 0; i < m_fb->getColorBufferCount(); ++i)
+		{
+			if(!!(m_colorWriteMasks[i]) && (m_enableBlendMask & (1 << i)))
+			{
+				enable = true;
+			}
+		}
+
+		if(enable != m_enableBlend)
+		{
+			m_enableBlend = enable;
+			return true;
+		}
+		return false;
+	}
+
+	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}};
+
+	U8 m_enableBlendMask = 0; ///< Per attachment
+
+	Bool setBlendMethods(U32 attachment, BlendMethod src, BlendMethod dst)
+	{
+		if(m_blendSrcMethod[attachment] != src || m_blendDstMethod[attachment] != dst)
+		{
+			m_blendSrcMethod[attachment] = src;
+			m_blendDstMethod[attachment] = dst;
+			Bool wantBlend = !(src == BlendMethod::ONE && dst == BlendMethod::ZERO);
+			m_enableBlendMask |= (wantBlend) ? (1 << attachment) : 0;
+			return true;
+		}
+		return false;
+	}
+
+	Array<BlendFunction, MAX_COLOR_ATTACHMENTS> m_blendFuncs = {
+		{BlendFunction::COUNT, BlendFunction::COUNT, BlendFunction::COUNT, BlendFunction::COUNT}};
+
+	Bool setBlendFunction(U32 attachment, BlendFunction func)
+	{
+		if(m_blendFuncs[attachment] != func)
+		{
+			m_blendFuncs[attachment] = func;
+			return true;
+		}
+		return false;
+	}
+	/// @}
+
+	/// @name resources
+	/// @{
+	class TextureBinding
+	{
+	public:
+		TextureImpl* m_tex = nullptr;
+		SamplerImpl* m_sampler = reinterpret_cast<SamplerImpl*>(0x1);
+		DepthStencilAspectMask m_aspect;
+	};
+
+	Array2d<TextureBinding, MAX_BOUND_RESOURCE_GROUPS, MAX_TEXTURE_BINDINGS> m_textures;
+
+	Bool bindTexture(
+		U32 set, U32 binding, TexturePtr tex, DepthStencilAspectMask aspect, Bool& texChanged, Bool& samplerChanged)
+	{
+		TextureBinding& b = m_textures[set][binding];
+		TextureImpl* texi = tex->m_impl.get();
+
+		texChanged = false;
+		samplerChanged = false;
+
+		if(texi != b.m_tex)
+		{
+			b.m_tex = texi;
+			texChanged = true;
+		}
+
+		if(b.m_sampler != nullptr)
+		{
+			b.m_sampler = nullptr;
+			samplerChanged = true;
+		}
+
+		b.m_aspect = aspect;
+		return samplerChanged || texChanged;
+	}
+
+	Bool bindTextureAndSampler(U32 set, U32 binding, TexturePtr tex, SamplerPtr sampler, DepthStencilAspectMask aspect)
+	{
+		TextureBinding& b = m_textures[set][binding];
+		b.m_tex = tex->m_impl.get();
+		b.m_sampler = sampler->m_impl.get();
+		b.m_aspect = aspect;
+		return true;
+	}
+
+	class ShaderBufferBinding
+	{
+	public:
+		BufferImpl* m_buff = nullptr;
+		PtrSize m_offset;
+		TransientMemoryToken m_token;
+	};
+
+	Array2d<ShaderBufferBinding, MAX_BOUND_RESOURCE_GROUPS, MAX_UNIFORM_BUFFER_BINDINGS> m_ubos;
+
+	Bool bindUniformBuffer(U32 set, U32 binding, BufferPtr buff, PtrSize offset)
+	{
+		ShaderBufferBinding& b = m_ubos[set][binding];
+		b.m_buff = buff->m_impl.get();
+		b.m_offset = offset;
+		b.m_token = {};
+		return true;
+	}
+
+	Bool bindUniformBuffer(U32 set, U32 binding, const TransientMemoryToken& token)
+	{
+		ShaderBufferBinding& b = m_ubos[set][binding];
+		b.m_buff = nullptr;
+		b.m_offset = 0;
+		b.m_token = token;
+		return true;
+	}
+
+	Array2d<ShaderBufferBinding, MAX_BOUND_RESOURCE_GROUPS, MAX_STORAGE_BUFFER_BINDINGS> m_ssbos;
+
+	Bool bindStorageBuffer(U32 set, U32 binding, BufferPtr buff, PtrSize offset)
+	{
+		ShaderBufferBinding& b = m_ssbos[set][binding];
+		b.m_buff = buff->m_impl.get();
+		b.m_offset = offset;
+		b.m_token = {};
+		return true;
+	}
+
+	Bool bindStorageBuffer(U32 set, U32 binding, const TransientMemoryToken& token)
+	{
+		ShaderBufferBinding& b = m_ssbos[set][binding];
+		b.m_buff = nullptr;
+		b.m_offset = 0;
+		b.m_token = token;
+		return true;
+	}
+
+	class ImageBinding
+	{
+	public:
+		TextureImpl* m_tex = nullptr;
+		U8 m_level;
+	};
+
+	Array2d<ImageBinding, MAX_BOUND_RESOURCE_GROUPS, MAX_IMAGE_BINDINGS> m_images;
+
+	Bool bindImage(U32 set, U32 binding, TexturePtr img, U32 level)
+	{
+		ImageBinding& b = m_images[set][binding];
+		b.m_tex = img->m_impl.get();
+		b.m_level = level;
+		return true;
+	}
+
+	ShaderProgramImpl* m_prog = nullptr;
+
+	Bool bindShaderProgram(ShaderProgramPtr prog)
+	{
+		if(prog->m_impl.get() != m_prog)
+		{
+			m_prog = prog->m_impl.get();
+			return true;
+		}
+		return false;
+	}
+	/// @}
+
+	/// @name other
+	/// @{
+	FramebufferImpl* m_fb = nullptr;
+
+	Bool beginRenderPass(const FramebufferPtr& fb)
+	{
+		ANKI_ASSERT(!insideRenderPass() && "Already inside a renderpass");
+		m_fb = fb->m_impl.get();
+		m_lastSecondLevelCmdb = nullptr;
+		return true;
+	}
+
+	void endRenderPass()
+	{
+		ANKI_ASSERT(insideRenderPass() && "Not inside a renderpass");
+		if(m_lastSecondLevelCmdb)
+		{
+			// Renderpass had 2nd level cmdbs, need to restore the state back to default
+			::new(this) StateTracker();
+		}
+		else
+		{
+			m_fb = nullptr;
+		}
+	}
+
+	Bool insideRenderPass() const
+	{
+		return m_fb != nullptr;
+	}
+
+	CommandBufferImpl* m_lastSecondLevelCmdb = nullptr;
+	/// @}
+
+	/// @name drawcalls
+	/// @{
+	void checkIndexedDracall() const
+	{
+		ANKI_ASSERT(m_idx.m_indexType != 0 && "Forgot to bind index buffer");
+		checkDrawcall();
+	}
+
+	void checkNonIndexedDrawcall() const
+	{
+		checkDrawcall();
+	}
+
+	void checkDrawcall() const
+	{
+		ANKI_ASSERT(m_viewport[1] != MAX_U16 && "Forgot to set the viewport");
+		ANKI_ASSERT(m_prog && "Forgot to bound a program");
+		ANKI_ASSERT((insideRenderPass() || m_secondLevel) && "Forgot to begin a render pass");
+	}
+
+	void checkDispatch() const
+	{
+		ANKI_ASSERT(m_prog && "Forgot to bound a program");
+		ANKI_ASSERT(!insideRenderPass() && "Forgot to end the render pass");
+	}
+	/// @}
+};
+/// @}
+
+} // end namespace anki

+ 24 - 47
src/anki/renderer/Bloom.cpp

@@ -45,32 +45,17 @@ Error BloomExposure::init(const ConfigSet& config)
 	m_fb = gr.newInstance<Framebuffer>(fbInit);
 
 	// init shaders
-	StringAuto pps(getAllocator());
-	pps.sprintf("#define WIDTH %u\n"
-				"#define HEIGHT %u\n"
-				"#define MIPMAP %u.0\n",
+	ANKI_CHECK(m_r->createShaderf("shaders/Bloom.frag.glsl",
+		m_frag,
+		"#define WIDTH %u\n"
+		"#define HEIGHT %u\n"
+		"#define MIPMAP %u.0\n",
 		m_r->getWidth() >> (m_r->getIs().getRtMipmapCount() - 1),
 		m_r->getHeight() >> (m_r->getIs().getRtMipmapCount() - 1),
-		m_r->getIs().getRtMipmapCount() - 1);
+		m_r->getIs().getRtMipmapCount() - 1));
 
-	ANKI_CHECK(getResourceManager().loadResourceToCache(m_frag, "shaders/Bloom.frag.glsl", pps.toCString(), "r_"));
-
-	// Init pplines
-	ColorStateInfo colorInf;
-	colorInf.m_attachmentCount = 1;
-	colorInf.m_attachments[0].m_format = BLOOM_RT_PIXEL_FORMAT;
-
-	m_r->createDrawQuadPipeline(m_frag->getGrShader(), colorInf, m_ppline);
-
-	// Set descriptors
-	ResourceGroupInitInfo descInit;
-	descInit.m_textures[0].m_texture = m_r->getIs().getRt();
-	descInit.m_uniformBuffers[0].m_uploadedMemory = true;
-	descInit.m_uniformBuffers[0].m_usage = BufferUsageBit::UNIFORM_FRAGMENT;
-	descInit.m_storageBuffers[0].m_buffer = m_r->getTm().getAverageLuminanceBuffer();
-	descInit.m_storageBuffers[0].m_usage = BufferUsageBit::STORAGE_FRAGMENT_READ;
-
-	m_rsrc = gr.newInstance<ResourceGroup>(descInit);
+	// Init prog
+	m_r->createDrawQuadShaderProgram(m_frag->getGrShader(), m_prog);
 
 	return ErrorCode::NONE;
 }
@@ -95,14 +80,16 @@ void BloomExposure::run(RenderingContext& ctx)
 
 	cmdb->beginRenderPass(m_fb);
 	cmdb->setViewport(0, 0, m_width, m_height);
-	cmdb->bindPipeline(m_ppline);
+	cmdb->bindShaderProgram(m_prog);
+	cmdb->bindTexture(0, 0, m_r->getIs().getRt());
 
-	TransientMemoryInfo dyn;
-	Vec4* uniforms = static_cast<Vec4*>(getGrManager().allocateFrameTransientMemory(
-		sizeof(Vec4), BufferUsageBit::UNIFORM_ALL, dyn.m_uniformBuffers[0]));
+	TransientMemoryToken token;
+	Vec4* uniforms = static_cast<Vec4*>(
+		getGrManager().allocateFrameTransientMemory(sizeof(Vec4), BufferUsageBit::UNIFORM_ALL, token));
 	*uniforms = Vec4(m_threshold, m_scale, 0.0, 0.0);
 
-	cmdb->bindResourceGroup(m_rsrc, 0, &dyn);
+	cmdb->bindUniformBuffer(0, 0, token);
+	cmdb->bindStorageBuffer(0, 0, m_r->getTm().m_luminanceBuff, 0);
 
 	m_r->drawQuad(cmdb);
 	cmdb->endRenderPass();
@@ -137,25 +124,15 @@ Error BloomUpscale::init(const ConfigSet& config)
 	m_fb = gr.newInstance<Framebuffer>(fbInit);
 
 	// init shaders
-	StringAuto pps(getAllocator());
-	pps.sprintf("#define WIDTH %u\n"
-				"#define HEIGHT %u\n",
+	ANKI_CHECK(m_r->createShaderf("shaders/BloomUpscale.frag.glsl",
+		m_frag,
+		"#define WIDTH %u\n"
+		"#define HEIGHT %u\n",
 		m_width,
-		m_height);
-
-	ANKI_CHECK(
-		getResourceManager().loadResourceToCache(m_frag, "shaders/BloomUpscale.frag.glsl", pps.toCString(), "r_"));
-
-	// Init pplines
-	ColorStateInfo colorInf;
-	colorInf.m_attachmentCount = 1;
-	colorInf.m_attachments[0].m_format = BLOOM_RT_PIXEL_FORMAT;
-	m_r->createDrawQuadPipeline(m_frag->getGrShader(), colorInf, m_ppline);
+		m_height));
 
-	// Set descriptors
-	ResourceGroupInitInfo descInit;
-	descInit.m_textures[0].m_texture = m_r->getBloom().m_extractExposure.m_rt;
-	m_rsrc = gr.newInstance<ResourceGroup>(descInit);
+	// Init prog
+	m_r->createDrawQuadShaderProgram(m_frag->getGrShader(), m_prog);
 
 	return ErrorCode::NONE;
 }
@@ -182,8 +159,8 @@ void BloomUpscale::run(RenderingContext& ctx)
 
 	cmdb->setViewport(0, 0, m_width, m_height);
 	cmdb->beginRenderPass(m_fb);
-	cmdb->bindPipeline(m_ppline);
-	cmdb->bindResourceGroup(m_rsrc, 0, nullptr);
+	cmdb->bindShaderProgram(m_prog);
+	cmdb->bindTexture(0, 0, m_r->getBloom().m_extractExposure.m_rt);
 	m_r->drawQuad(cmdb);
 
 	m_r->getBloom().m_sslf.run(ctx);

+ 14 - 4
src/anki/renderer/Bloom.h

@@ -46,8 +46,7 @@ anki_internal:
 private:
 	FramebufferPtr m_fb;
 	ShaderResourcePtr m_frag;
-	PipelinePtr m_ppline;
-	ResourceGroupPtr m_rsrc;
+	ShaderProgramPtr m_prog;
 
 	F32 m_threshold = 10.0; ///< How bright it is
 	F32 m_scale = 1.0;
@@ -78,8 +77,7 @@ anki_internal:
 private:
 	FramebufferPtr m_fb;
 	ShaderResourcePtr m_frag;
-	PipelinePtr m_ppline;
-	ResourceGroupPtr m_rsrc;
+	ShaderProgramPtr m_prog;
 };
 
 /// Bloom pass.
@@ -103,6 +101,18 @@ anki_internal:
 	}
 
 	ANKI_USE_RESULT Error init(const ConfigSet& cfg)
+	{
+		ANKI_LOGI("Initializing bloom passes");
+		Error err = initInternal(cfg);
+		if(err)
+		{
+			ANKI_LOGE("Failed to initialize bloom passes");
+		}
+		return err;
+	}
+
+private:
+	ANKI_USE_RESULT Error initInternal(const ConfigSet& cfg)
 	{
 		ANKI_CHECK(m_extractExposure.init(cfg));
 		ANKI_CHECK(m_upscale.init(cfg));

+ 10 - 7
src/anki/renderer/Clusterer.h

@@ -52,15 +52,18 @@ public:
 	}
 
 private:
+	class S
+	{
+	public:
+		U8 m_x;
+		U8 m_y;
+		U8 m_z;
+		U8 m_pad_;
+	};
+
 	union
 	{
-		struct
-		{
-			U8 m_x;
-			U8 m_y;
-			U8 m_z;
-			U8 m_pad_;
-		} m_v;
+		S m_v;
 		U32 m_u32;
 	};
 };

+ 14 - 43
src/anki/renderer/DebugDrawer.cpp

@@ -31,38 +31,10 @@ Error DebugDrawer::init(Renderer* r)
 	m_r = r;
 	GrManager& gr = r->getGrManager();
 
-	// Create the pipelines
+	// Create the prog and shaders
 	ANKI_CHECK(r->getResourceManager().loadResource("shaders/Dbg.vert.glsl", m_vert));
 	ANKI_CHECK(r->getResourceManager().loadResource("shaders/Dbg.frag.glsl", m_frag));
-
-	PipelineInitInfo init;
-	init.m_vertex.m_bindingCount = 1;
-	init.m_vertex.m_bindings[0].m_stride = 2 * sizeof(Vec4);
-	init.m_vertex.m_attributeCount = 2;
-	init.m_vertex.m_attributes[0].m_format = PixelFormat(ComponentFormat::R32G32B32A32, TransformFormat::FLOAT);
-	init.m_vertex.m_attributes[0].m_offset = 0;
-	init.m_vertex.m_attributes[0].m_binding = 0;
-	init.m_vertex.m_attributes[1].m_format = PixelFormat(ComponentFormat::R32G32B32A32, TransformFormat::FLOAT);
-	init.m_vertex.m_attributes[1].m_offset = sizeof(Vec4);
-	init.m_vertex.m_attributes[1].m_binding = 0;
-	init.m_inputAssembler.m_topology = PrimitiveTopology::LINES;
-	init.m_depthStencil.m_depthWriteEnabled = false;
-	init.m_depthStencil.m_format = MS_DEPTH_ATTACHMENT_PIXEL_FORMAT;
-	init.m_color.m_attachmentCount = 1;
-	init.m_color.m_attachments[0].m_format = DBG_COLOR_ATTACHMENT_PIXEL_FORMAT;
-	init.m_shaders[U(ShaderType::VERTEX)] = m_vert->getGrShader();
-	init.m_shaders[U(ShaderType::FRAGMENT)] = m_frag->getGrShader();
-
-	getPpline(true, PrimitiveTopology::LINES) = gr.newInstance<Pipeline>(init);
-
-	init.m_inputAssembler.m_topology = PrimitiveTopology::TRIANGLES;
-	getPpline(true, PrimitiveTopology::TRIANGLES) = gr.newInstance<Pipeline>(init);
-
-	init.m_depthStencil.m_depthCompareFunction = CompareOperation::ALWAYS;
-	getPpline(false, PrimitiveTopology::TRIANGLES) = gr.newInstance<Pipeline>(init);
-
-	init.m_inputAssembler.m_topology = PrimitiveTopology::LINES;
-	getPpline(false, PrimitiveTopology::LINES) = gr.newInstance<Pipeline>(init);
+	m_prog = gr.newInstance<ShaderProgram>(m_vert->getGrShader(), m_frag->getGrShader());
 
 	// Create the vert buffs
 	for(BufferPtr& v : m_vertBuff)
@@ -71,15 +43,6 @@ Error DebugDrawer::init(Renderer* r)
 			sizeof(Vertex) * MAX_VERTS_PER_FRAME, BufferUsageBit::VERTEX, BufferMapAccessBit::WRITE);
 	}
 
-	// Create the resouce groups
-	U c = 0;
-	for(ResourceGroupPtr& rc : m_rcGroup)
-	{
-		ResourceGroupInitInfo rcinit;
-		rcinit.m_vertexBuffers[0].m_buffer = m_vertBuff[c++];
-		rc = gr.newInstance<ResourceGroup>(rcinit);
-	}
-
 	m_mMat.setIdentity();
 	m_vpMat.setIdentity();
 	m_mvpMat.setIdentity();
@@ -95,7 +58,11 @@ void DebugDrawer::prepareFrame(CommandBufferPtr& jobs)
 	void* mapped = m_vertBuff[frame]->map(0, MAX_VERTS_PER_FRAME * sizeof(Vertex), BufferMapAccessBit::WRITE);
 	m_clientVerts = WeakArray<Vertex>(static_cast<Vertex*>(mapped), MAX_VERTS_PER_FRAME);
 
-	m_cmdb->bindResourceGroup(m_rcGroup[frame], 0, nullptr);
+	m_cmdb->bindVertexBuffer(0, m_vertBuff[frame], 0, 2 * sizeof(Vec4));
+	m_cmdb->setVertexAttribute(0, 0, PixelFormat(ComponentFormat::R32G32B32A32, TransformFormat::FLOAT), 0);
+	m_cmdb->setVertexAttribute(1, 0, PixelFormat(ComponentFormat::R32G32B32A32, TransformFormat::FLOAT), sizeof(Vec4));
+
+	m_cmdb->bindShaderProgram(m_prog);
 
 	m_frameVertCount = 0;
 	m_crntDrawVertCount = 0;
@@ -108,7 +75,10 @@ void DebugDrawer::finishFrame()
 
 	flush();
 
-	m_cmdb = CommandBufferPtr(); // Release job chain
+	// Restore state
+	m_cmdb->setDepthCompareFunction(CompareOperation::ALWAYS);
+
+	m_cmdb = CommandBufferPtr(); // Release command buffer
 }
 
 void DebugDrawer::setModelMatrix(const Mat4& m)
@@ -168,9 +138,10 @@ void DebugDrawer::flush()
 			ANKI_ASSERT((m_crntDrawVertCount % 3) == 0);
 		}
 
-		m_cmdb->bindPipeline(getPpline(m_depthTestEnabled, m_primitive));
+		m_cmdb->setDepthCompareFunction((m_depthTestEnabled) ? CompareOperation::LESS : CompareOperation::ALWAYS);
+
 		U firstVert = m_frameVertCount - m_crntDrawVertCount;
-		m_cmdb->drawArrays(m_crntDrawVertCount, 1, firstVert);
+		m_cmdb->drawArrays(m_primitive, m_crntDrawVertCount, 1, firstVert);
 
 		m_crntDrawVertCount = 0;
 	}

+ 1 - 9
src/anki/renderer/DebugDrawer.h

@@ -88,8 +88,7 @@ private:
 	Renderer* m_r;
 	ShaderResourcePtr m_frag;
 	ShaderResourcePtr m_vert;
-	Array2d<PipelinePtr, 2, 2> m_pplines;
-	Array<ResourceGroupPtr, MAX_FRAMES_IN_FLIGHT> m_rcGroup;
+	ShaderProgramPtr m_prog;
 	Array<BufferPtr, MAX_FRAMES_IN_FLIGHT> m_vertBuff;
 
 	CommandBufferPtr m_cmdb;
@@ -107,13 +106,6 @@ private:
 
 	Bool8 m_depthTestEnabled = true;
 
-	PipelinePtr& getPpline(Bool depth, PrimitiveTopology topology)
-	{
-		U i = (depth == false) ? 0 : 1;
-		U j = (topology == PrimitiveTopology::LINES) ? 0 : 1;
-		return m_pplines[i][j];
-	}
-
 	void flush();
 };
 

+ 30 - 33
src/anki/renderer/DepthDownscale.cpp

@@ -38,13 +38,6 @@ Error HalfDepth::init(const ConfigSet&)
 
 	m_fb = gr.newInstance<Framebuffer>(fbInit);
 
-	// Create RC group
-	ResourceGroupInitInfo rcinit;
-	rcinit.m_textures[0].m_texture = m_r->getMs().m_depthRt;
-	rcinit.m_textures[0].m_usage = TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ;
-
-	m_rcgroup = gr.newInstance<ResourceGroup>(rcinit);
-
 	return ErrorCode::NONE;
 }
 
@@ -69,13 +62,18 @@ void HalfDepth::run(RenderingContext& ctx)
 	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
 
 	cmdb->beginRenderPass(m_fb);
+	cmdb->bindShaderProgram(m_parent->m_prog);
+	cmdb->bindTexture(0, 0, m_r->getMs().m_depthRt);
+
 	cmdb->setViewport(0, 0, m_r->getWidth() / 2, m_r->getHeight() / 2);
-	cmdb->bindPipeline(m_parent->m_ppline);
-	cmdb->bindResourceGroup(m_rcgroup, 0, nullptr);
+	cmdb->setDepthCompareFunction(CompareOperation::ALWAYS);
 
 	m_r->drawQuad(cmdb);
 
 	cmdb->endRenderPass();
+
+	// Restore state
+	cmdb->setDepthCompareFunction(CompareOperation::LESS);
 }
 
 QuarterDepth::~QuarterDepth()
@@ -106,13 +104,6 @@ Error QuarterDepth::init(const ConfigSet&)
 
 	m_fb = gr.newInstance<Framebuffer>(fbInit);
 
-	// Create RC group
-	ResourceGroupInitInfo rcinit;
-	rcinit.m_textures[0].m_texture = m_parent->m_hd.m_depthRt;
-	rcinit.m_textures[0].m_usage = TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ;
-
-	m_rcgroup = gr.newInstance<ResourceGroup>(rcinit);
-
 	return ErrorCode::NONE;
 }
 
@@ -137,42 +128,48 @@ void QuarterDepth::run(RenderingContext& ctx)
 	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
 
 	cmdb->beginRenderPass(m_fb);
+	cmdb->bindShaderProgram(m_parent->m_prog);
+	cmdb->bindTexture(0, 0, m_parent->m_hd.m_depthRt);
+
 	cmdb->setViewport(0, 0, m_r->getWidth() / 4, m_r->getHeight() / 4);
-	cmdb->bindPipeline(m_parent->m_ppline);
-	cmdb->bindResourceGroup(m_rcgroup, 0, nullptr);
+	cmdb->setDepthCompareFunction(CompareOperation::ALWAYS);
 
 	m_r->drawQuad(cmdb);
 
 	cmdb->endRenderPass();
+
+	// Restore state
+	cmdb->setDepthCompareFunction(CompareOperation::LESS);
 }
 
 DepthDownscale::~DepthDownscale()
 {
 }
 
-Error DepthDownscale::init(const ConfigSet& cfg)
+Error DepthDownscale::initInternal(const ConfigSet& cfg)
 {
-	GrManager& gr = getGrManager();
-
 	// Create shader
 	ANKI_CHECK(getResourceManager().loadResource("shaders/DepthDownscale.frag.glsl", m_frag));
 
-	// Create pipeline
-	PipelineInitInfo pinit;
-
-	pinit.m_inputAssembler.m_topology = PrimitiveTopology::TRIANGLE_STRIP;
-
-	pinit.m_depthStencil.m_depthWriteEnabled = true;
-	pinit.m_depthStencil.m_depthCompareFunction = CompareOperation::ALWAYS;
-	pinit.m_depthStencil.m_format = MS_DEPTH_ATTACHMENT_PIXEL_FORMAT;
-
-	pinit.m_shaders[ShaderType::VERTEX] = m_r->getDrawQuadVertexShader();
-	pinit.m_shaders[ShaderType::FRAGMENT] = m_frag->getGrShader();
-	m_ppline = gr.newInstance<Pipeline>(pinit);
+	// Create prog
+	m_r->createDrawQuadShaderProgram(m_frag->getGrShader(), m_prog);
 
 	ANKI_CHECK(m_hd.init(cfg));
 	ANKI_CHECK(m_qd.init(cfg));
 	return ErrorCode::NONE;
 }
 
+Error DepthDownscale::init(const ConfigSet& cfg)
+{
+	ANKI_LOGI("Initializing depth downscale passes");
+
+	Error err = initInternal(cfg);
+	if(err)
+	{
+		ANKI_LOGE("Failed to initialize depth downscale passes");
+	}
+
+	return err;
+}
+
 } // end namespace anki

+ 3 - 3
src/anki/renderer/DepthDownscale.h

@@ -42,7 +42,6 @@ anki_internal:
 private:
 	DepthDownscale* m_parent;
 
-	ResourceGroupPtr m_rcgroup;
 	FramebufferPtr m_fb;
 };
 
@@ -69,7 +68,6 @@ anki_internal:
 private:
 	DepthDownscale* m_parent;
 
-	ResourceGroupPtr m_rcgroup;
 	FramebufferPtr m_fb;
 };
 
@@ -95,7 +93,9 @@ anki_internal:
 
 private:
 	ShaderResourcePtr m_frag;
-	PipelinePtr m_ppline;
+	ShaderProgramPtr m_prog;
+
+	ANKI_USE_RESULT Error initInternal(const ConfigSet& cfg);
 };
 /// @}
 

+ 25 - 32
src/anki/renderer/DownscaleBlur.cpp

@@ -19,34 +19,17 @@ Error DownscaleBlur::initSubpass(U idx, const UVec2& inputTexSize)
 {
 	Subpass& pass = m_passes[idx];
 
-	PipelineInitInfo ppinit;
-	ppinit.m_color.m_attachmentCount = 1;
-	ppinit.m_color.m_attachments[0].m_format = IS_COLOR_ATTACHMENT_PIXEL_FORMAT;
-	ppinit.m_depthStencil.m_depthWriteEnabled = false;
-	ppinit.m_depthStencil.m_depthCompareFunction = CompareOperation::ALWAYS;
-
-	StringAuto pps(getAllocator());
-
-	// vert shader
-	ANKI_CHECK(getResourceManager().loadResource("shaders/Quad.vert.glsl", pass.m_vert));
-
-	ppinit.m_shaders[ShaderType::VERTEX] = pass.m_vert->getGrShader();
-
 	// frag shader
-	pps.destroy();
-	pps.sprintf("#define TEXTURE_SIZE vec2(%f, %f)\n"
-				"#define TEXTURE_MIPMAP float(%u)\n",
+	ANKI_CHECK(m_r->createShaderf("shaders/DownscaleBlur.frag.glsl",
+		pass.m_frag,
+		"#define TEXTURE_SIZE vec2(%f, %f)\n"
+		"#define TEXTURE_MIPMAP float(%u)\n",
 		F32(inputTexSize.x()),
 		F32(inputTexSize.y()),
-		idx);
+		idx));
 
-	ANKI_CHECK(getResourceManager().loadResourceToCache(
-		pass.m_frag, "shaders/DownscaleBlur.frag.glsl", pps.toCString(), "r_"));
-
-	ppinit.m_shaders[ShaderType::FRAGMENT] = pass.m_frag->getGrShader();
-
-	// ppline
-	pass.m_ppline = getGrManager().newInstance<Pipeline>(ppinit);
+	// prog
+	m_r->createDrawQuadShaderProgram(pass.m_frag->getGrShader(), pass.m_prog);
 
 	// FB
 	FramebufferInitInfo fbInit;
@@ -57,15 +40,23 @@ Error DownscaleBlur::initSubpass(U idx, const UVec2& inputTexSize)
 	fbInit.m_colorAttachments[0].m_usageInsideRenderPass = TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE;
 	pass.m_fb = getGrManager().newInstance<Framebuffer>(fbInit);
 
-	// Resources
-	ResourceGroupInitInfo rcinit;
-	rcinit.m_textures[0].m_texture = m_r->getIs().getRt();
-	pass.m_rcGroup = getGrManager().newInstance<ResourceGroup>(rcinit);
-
 	return ErrorCode::NONE;
 }
 
-Error DownscaleBlur::init(const ConfigSet& initializer)
+Error DownscaleBlur::init(const ConfigSet& cfg)
+{
+	ANKI_LOGI("Initializing dowscale blur");
+
+	Error err = initInternal(cfg);
+	if(err)
+	{
+		ANKI_LOGE("Failed to initialize downscale blur");
+	}
+
+	return err;
+}
+
+Error DownscaleBlur::initInternal(const ConfigSet&)
 {
 	m_passes.create(getAllocator(), m_r->getIs().getRtMipmapCount() - 1);
 
@@ -82,6 +73,9 @@ Error DownscaleBlur::init(const ConfigSet& initializer)
 void DownscaleBlur::run(RenderingContext& ctx)
 {
 	CommandBufferPtr cmdb = ctx.m_commandBuffer;
+
+	cmdb->bindTexture(0, 0, m_r->getIs().getRt());
+
 	UVec2 size(m_r->getWidth(), m_r->getHeight());
 	for(U i = 0; i < m_passes.getSize(); ++i)
 	{
@@ -103,8 +97,7 @@ void DownscaleBlur::run(RenderingContext& ctx)
 
 		cmdb->beginRenderPass(pass.m_fb);
 		cmdb->setViewport(0, 0, size.x(), size.y());
-		cmdb->bindPipeline(pass.m_ppline);
-		cmdb->bindResourceGroup(pass.m_rcGroup, 0, nullptr);
+		cmdb->bindShaderProgram(pass.m_prog);
 
 		m_r->drawQuad(cmdb);
 		cmdb->endRenderPass();

+ 4 - 5
src/anki/renderer/DownscaleBlur.h

@@ -24,7 +24,7 @@ anki_internal:
 
 	~DownscaleBlur();
 
-	ANKI_USE_RESULT Error init(const ConfigSet& initializer);
+	ANKI_USE_RESULT Error init(const ConfigSet& cfg);
 
 	void run(RenderingContext& ctx);
 
@@ -32,16 +32,15 @@ private:
 	class Subpass
 	{
 	public:
-		ShaderResourcePtr m_vert;
 		ShaderResourcePtr m_frag;
-		PipelinePtr m_ppline;
-		ResourceGroupPtr m_rcGroup;
+		ShaderProgramPtr m_prog;
 		FramebufferPtr m_fb;
 	};
 
 	DynamicArray<Subpass> m_passes;
 
-	Error initSubpass(U idx, const UVec2& inputTexSize);
+	ANKI_USE_RESULT Error initInternal(const ConfigSet& cfg);
+	ANKI_USE_RESULT Error initSubpass(U idx, const UVec2& inputTexSize);
 };
 /// @}
 

+ 88 - 62
src/anki/renderer/Drawer.cpp

@@ -4,7 +4,6 @@
 // http://www.anki3d.org/LICENSE
 
 #include <anki/renderer/Drawer.h>
-#include <anki/renderer/Ms.h>
 #include <anki/resource/ShaderResource.h>
 #include <anki/scene/FrustumComponent.h>
 #include <anki/resource/Material.h>
@@ -30,43 +29,60 @@ static Bool canMergeBuildInfo(const RenderingBuildInfoOut& a, const RenderingBui
 
 	ANKI_ASSERT(a.m_hasTransform == b.m_hasTransform);
 
-	if(a.m_resourceGroup != b.m_resourceGroup)
+	if(a.m_program != b.m_program)
 	{
 		return false;
 	}
 
-	for(U i = 0; i < U(ShaderType::COUNT); ++i)
+	if(a.m_vertexBufferBindingCount != b.m_vertexBufferBindingCount
+		|| a.m_vertexAttributeCount != b.m_vertexAttributeCount)
 	{
-		if(a.m_state->m_shaders[i] != b.m_state->m_shaders[i])
+		return false;
+	}
+
+	for(U i = 0; i < a.m_vertexBufferBindingCount; ++i)
+	{
+		if(a.m_vertexBufferBindings[i] != b.m_vertexBufferBindings[i])
 		{
 			return false;
 		}
 	}
 
-	// Drawcall
-	if(a.m_drawArrays != b.m_drawArrays)
+	for(U i = 0; i < a.m_vertexAttributeCount; ++i)
+	{
+		if(a.m_vertexAttributes[i] != b.m_vertexAttributes[i])
+		{
+			return false;
+		}
+	}
+
+	if(a.m_indexBuffer != b.m_indexBuffer)
 	{
 		return false;
 	}
 
-	if(a.m_drawArrays && a.m_drawcall.m_arrays != b.m_drawcall.m_arrays)
+	if(a.m_indexBufferToken != b.m_indexBufferToken)
 	{
 		return false;
 	}
 
-	if(!a.m_drawArrays && a.m_drawcall.m_elements != b.m_drawcall.m_elements)
+	// Drawcall
+	if(a.m_drawArrays != b.m_drawArrays)
 	{
 		return false;
 	}
 
-	// Vertex
-	if(a.m_state->m_vertex != b.m_state->m_vertex)
+	if(a.m_drawArrays && a.m_drawcall.m_arrays != b.m_drawcall.m_arrays)
+	{
+		return false;
+	}
+
+	if(!a.m_drawArrays && a.m_drawcall.m_elements != b.m_drawcall.m_elements)
 	{
 		return false;
 	}
 
-	// IA
-	if(a.m_state->m_inputAssembler != b.m_state->m_inputAssembler)
+	if(a.m_topology != b.m_topology)
 	{
 		return false;
 	}
@@ -76,15 +92,18 @@ static Bool canMergeBuildInfo(const RenderingBuildInfoOut& a, const RenderingBui
 
 static void resetRenderingBuildInfoOut(RenderingBuildInfoOut& b)
 {
-	b.m_resourceGroup.reset(nullptr);
-	b.m_drawcall.m_elements = DrawElementsIndirectInfo();
-	b.m_drawArrays = false;
 	b.m_hasTransform = false;
-	b.m_stateMask = PipelineSubStateBit::NONE;
+	b.m_program = {};
+
+	b.m_vertexBufferBindingCount = 0;
+	b.m_vertexAttributeCount = 0;
+
+	b.m_indexBuffer = {};
+	b.m_indexBufferToken = {};
 
-	b.m_state->m_inputAssembler = InputAssemblerStateInfo();
-	b.m_state->m_vertex = VertexStateInfo();
-	b.m_state->m_shaders = Array<ShaderPtr, U(ShaderType::COUNT)>();
+	b.m_drawcall.m_elements = DrawElementsIndirectInfo();
+	b.m_drawArrays = false;
+	b.m_topology = PrimitiveTopology::TRIANGLES;
 }
 
 class CompleteRenderingBuildInfo
@@ -94,11 +113,6 @@ public:
 	RenderComponent* m_rc = nullptr;
 	RenderingBuildInfoIn m_in;
 	RenderingBuildInfoOut m_out;
-
-	CompleteRenderingBuildInfo(PipelineInitInfo* state)
-		: m_out(state)
-	{
-	}
 };
 
 /// Drawer's context
@@ -114,19 +128,12 @@ public:
 	Array<Mat4, MAX_INSTANCES> m_cachedTrfs;
 	U m_cachedTrfCount = 0;
 
-	TransientMemoryInfo m_dynBufferInfo;
-	U m_nodeProcessedCount = 0;
+	TransientMemoryToken m_uboToken;
 
-	GrObjectCache* m_pplineCache;
+	U m_nodeProcessedCount = 0;
 
 	Array<CompleteRenderingBuildInfo, 2> m_buildInfo;
-	Array<PipelineInitInfo, 2> m_state;
 	U m_crntBuildInfo = 0;
-
-	DrawContext()
-		: m_buildInfo{{&m_state[0], &m_state[1]}}
-	{
-	}
 };
 
 /// Visitor that sets a uniform
@@ -276,7 +283,8 @@ void SetupRenderableVariableVisitor::uniSet<TextureResourcePtr>(
 	const MaterialVariable& mtlvar, const TextureResourcePtr* values, U32 size)
 {
 	ANKI_ASSERT(size == 1);
-	// Do nothing
+	ANKI_ASSERT(values);
+	m_ctx->m_cmdb->bindTexture(0, mtlvar.getTextureUnit(), (*values)->getGrTexture());
 }
 
 RenderableDrawer::~RenderableDrawer()
@@ -290,7 +298,7 @@ void RenderableDrawer::setupUniforms(DrawContext& ctx, CompleteRenderingBuildInf
 
 	// Get some memory for uniforms
 	U8* uniforms = static_cast<U8*>(m_r->getGrManager().allocateFrameTransientMemory(
-		variant.getDefaultBlockSize(), BufferUsageBit::UNIFORM_ALL, ctx.m_dynBufferInfo.m_uniformBuffers[0]));
+		variant.getDefaultBlockSize(), BufferUsageBit::UNIFORM_ALL, ctx.m_uboToken));
 
 	// Call the visitor
 	SetupRenderableVariableVisitor visitor;
@@ -312,13 +320,8 @@ void RenderableDrawer::setupUniforms(DrawContext& ctx, CompleteRenderingBuildInf
 	}
 }
 
-Error RenderableDrawer::drawRange(Pass pass,
-	const FrustumComponent& frc,
-	CommandBufferPtr cmdb,
-	GrObjectCache& pplineCache,
-	const PipelineInitInfo& state,
-	VisibleNode* begin,
-	VisibleNode* end)
+Error RenderableDrawer::drawRange(
+	Pass pass, const FrustumComponent& frc, CommandBufferPtr cmdb, VisibleNode* begin, VisibleNode* end)
 {
 	ANKI_ASSERT(begin && end && begin < end);
 
@@ -326,9 +329,6 @@ Error RenderableDrawer::drawRange(Pass pass,
 	ctx.m_frc = &frc;
 	ctx.m_pass = pass;
 	ctx.m_cmdb = cmdb;
-	ctx.m_pplineCache = &pplineCache;
-	ctx.m_state[0] = state;
-	ctx.m_state[1] = state;
 
 	for(; begin != end; ++begin)
 	{
@@ -357,35 +357,63 @@ Error RenderableDrawer::flushDrawcall(DrawContext& ctx, CompleteRenderingBuildIn
 		ANKI_CHECK(rc.buildRendering(build.m_in, build.m_out));
 	}
 
-	// Create the pipeline
-	U64 pplineHash = build.m_out.m_state->computeHash();
-	PipelinePtr ppline;
-	Bool pplineFound = rc.tryGetPipeline(pplineHash, ppline);
+	// Enqueue uniform state updates
+	setupUniforms(ctx, build);
+
+	// Finaly, touch the command buffer
+	CommandBufferPtr& cmdb = ctx.m_cmdb;
+
+	cmdb->bindUniformBuffer(0, 0, ctx.m_uboToken);
+	cmdb->bindShaderProgram(build.m_out.m_program);
 
-	if(ANKI_UNLIKELY(!pplineFound))
+	for(U i = 0; i < build.m_out.m_vertexBufferBindingCount; ++i)
 	{
-		ppline = ctx.m_pplineCache->newInstance<Pipeline>(*build.m_out.m_state, pplineHash);
-		rc.storePipeline(pplineHash, ppline);
+		const RenderingVertexBufferBinding& binding = build.m_out.m_vertexBufferBindings[i];
+		if(binding.m_buffer)
+		{
+			cmdb->bindVertexBuffer(i, binding.m_buffer, binding.m_offset, binding.m_stride);
+		}
+		else
+		{
+			ANKI_ASSERT(!!(binding.m_token));
+			cmdb->bindVertexBuffer(i, binding.m_token, binding.m_stride);
+		}
 	}
 
-	// Enqueue uniform state updates
-	setupUniforms(ctx, build);
+	for(U i = 0; i < build.m_out.m_vertexAttributeCount; ++i)
+	{
+		const RenderingVertexAttributeInfo& attrib = build.m_out.m_vertexAttributes[i];
+
+		cmdb->setVertexAttribute(i, attrib.m_bufferBinding, attrib.m_format, attrib.m_relativeOffset);
+	}
 
-	// Finaly, touch the command buffer
-	ctx.m_cmdb->bindResourceGroup(build.m_out.m_resourceGroup, 0, &ctx.m_dynBufferInfo);
-	ctx.m_cmdb->bindPipeline(ppline);
 	if(!build.m_out.m_drawArrays)
 	{
 		const DrawElementsIndirectInfo& drawc = build.m_out.m_drawcall.m_elements;
 
-		ctx.m_cmdb->drawElements(
-			drawc.m_count, drawc.m_instanceCount, drawc.m_firstIndex, drawc.m_baseVertex, drawc.m_baseInstance);
+		if(build.m_out.m_indexBuffer)
+		{
+			cmdb->bindIndexBuffer(build.m_out.m_indexBuffer, 0, IndexType::U16);
+		}
+		else
+		{
+			ANKI_ASSERT(!!(build.m_out.m_indexBufferToken));
+			cmdb->bindIndexBuffer(build.m_out.m_indexBufferToken, IndexType::U16);
+		}
+
+		cmdb->drawElements(build.m_out.m_topology,
+			drawc.m_count,
+			drawc.m_instanceCount,
+			drawc.m_firstIndex,
+			drawc.m_baseVertex,
+			drawc.m_baseInstance);
 	}
 	else
 	{
 		const DrawArraysIndirectInfo& drawc = build.m_out.m_drawcall.m_arrays;
 
-		ctx.m_cmdb->drawArrays(drawc.m_count, drawc.m_instanceCount, drawc.m_first, drawc.m_baseInstance);
+		cmdb->drawArrays(
+			build.m_out.m_topology, drawc.m_count, drawc.m_instanceCount, drawc.m_first, drawc.m_baseInstance);
 	}
 
 	// Rendered something, reset the cached transforms
@@ -426,8 +454,6 @@ Error RenderableDrawer::drawSingle(DrawContext& ctx)
 
 	resetRenderingBuildInfoOut(crntBuild.m_out);
 	ANKI_CHECK(renderable.buildRendering(crntBuild.m_in, crntBuild.m_out));
-	ANKI_ASSERT(crntBuild.m_out.m_stateMask
-		== (PipelineSubStateBit::VERTEX | PipelineSubStateBit::INPUT_ASSEMBLER | PipelineSubStateBit::SHADERS));
 
 	if(ANKI_UNLIKELY(ctx.m_nodeProcessedCount == 0))
 	{

+ 2 - 7
src/anki/renderer/Drawer.h

@@ -34,13 +34,8 @@ public:
 
 	~RenderableDrawer();
 
-	ANKI_USE_RESULT Error drawRange(Pass pass,
-		const FrustumComponent& frc,
-		CommandBufferPtr cmdb,
-		GrObjectCache& pplineCache,
-		const PipelineInitInfo& state,
-		VisibleNode* begin,
-		VisibleNode* end);
+	ANKI_USE_RESULT Error drawRange(
+		Pass pass, const FrustumComponent& frc, CommandBufferPtr cmdb, VisibleNode* begin, VisibleNode* end);
 
 private:
 	Renderer* m_r;

+ 44 - 68
src/anki/renderer/Fs.cpp

@@ -18,13 +18,22 @@ namespace anki
 
 Fs::~Fs()
 {
-	if(m_pplineCache)
+}
+
+Error Fs::init(const ConfigSet& cfg)
+{
+	ANKI_LOGI("Initializing forward shading");
+
+	Error err = initInternal(cfg);
+	if(err)
 	{
-		getAllocator().deleteInstance(m_pplineCache);
+		ANKI_LOGE("Failed to initialize forward shading");
 	}
+
+	return err;
 }
 
-Error Fs::init(const ConfigSet&)
+Error Fs::initInternal(const ConfigSet&)
 {
 	m_width = m_r->getWidth() / FS_FRACTION;
 	m_height = m_r->getHeight() / FS_FRACTION;
@@ -50,43 +59,6 @@ Error Fs::init(const ConfigSet&)
 	fbInit.m_depthStencilAttachment.m_aspect = DepthStencilAspectMask::DEPTH;
 	m_fb = getGrManager().newInstance<Framebuffer>(fbInit);
 
-	// Init the global resources
-	{
-		ResourceGroupInitInfo init;
-		init.m_textures[0].m_texture = m_r->getDepthDownscale().m_hd.m_depthRt;
-		init.m_textures[0].m_usage = TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ;
-		init.m_textures[1].m_texture = m_r->getSm().getSpotTextureArray();
-		init.m_textures[2].m_texture = m_r->getSm().getOmniTextureArray();
-
-		init.m_uniformBuffers[0].m_uploadedMemory = true;
-		init.m_uniformBuffers[0].m_usage = BufferUsageBit::UNIFORM_FRAGMENT | BufferUsageBit::UNIFORM_VERTEX;
-		init.m_uniformBuffers[1].m_uploadedMemory = true;
-		init.m_uniformBuffers[1].m_usage = BufferUsageBit::UNIFORM_FRAGMENT | BufferUsageBit::UNIFORM_VERTEX;
-		init.m_uniformBuffers[2].m_uploadedMemory = true;
-		init.m_uniformBuffers[2].m_usage = BufferUsageBit::UNIFORM_FRAGMENT | BufferUsageBit::UNIFORM_VERTEX;
-
-		init.m_storageBuffers[0].m_uploadedMemory = true;
-		init.m_storageBuffers[0].m_usage = BufferUsageBit::STORAGE_FRAGMENT_READ | BufferUsageBit::STORAGE_VERTEX_READ;
-		init.m_storageBuffers[1].m_uploadedMemory = true;
-		init.m_storageBuffers[1].m_usage = BufferUsageBit::STORAGE_FRAGMENT_READ | BufferUsageBit::STORAGE_VERTEX_READ;
-
-		m_globalResources = getGrManager().newInstance<ResourceGroup>(init);
-	}
-
-	// Init state
-	{
-		ColorStateInfo& color = m_state.m_color;
-		color.m_attachmentCount = 1;
-		color.m_attachments[0].m_format = IS_COLOR_ATTACHMENT_PIXEL_FORMAT;
-		color.m_attachments[0].m_srcBlendMethod = BlendMethod::SRC_ALPHA;
-		color.m_attachments[0].m_dstBlendMethod = BlendMethod::ONE_MINUS_SRC_ALPHA;
-
-		m_state.m_depthStencil.m_format = MS_DEPTH_ATTACHMENT_PIXEL_FORMAT;
-		m_state.m_depthStencil.m_depthWriteEnabled = false;
-	}
-
-	m_pplineCache = getAllocator().newInstance<GrObjectCache>(&getGrManager());
-
 	ANKI_CHECK(initVol());
 
 	return ErrorCode::NONE;
@@ -94,32 +66,18 @@ Error Fs::init(const ConfigSet&)
 
 Error Fs::initVol()
 {
-	ANKI_CHECK(m_r->createShader("shaders/VolumetricUpscale.frag.glsl",
+	ANKI_CHECK(m_r->createShaderf("shaders/VolumetricUpscale.frag.glsl",
 		m_vol.m_frag,
 		"#define SRC_SIZE vec2(float(%u), float(%u))\n",
 		m_r->getWidth() / VOLUMETRIC_FRACTION,
 		m_r->getHeight() / VOLUMETRIC_FRACTION));
 
-	ColorStateInfo color;
-	color.m_attachmentCount = 1;
-	color.m_attachments[0].m_format = IS_COLOR_ATTACHMENT_PIXEL_FORMAT;
-	color.m_attachments[0].m_srcBlendMethod = BlendMethod::ONE;
-	color.m_attachments[0].m_dstBlendMethod = BlendMethod::ONE;
-	m_r->createDrawQuadPipeline(m_vol.m_frag->getGrShader(), color, m_vol.m_ppline);
+	m_r->createDrawQuadShaderProgram(m_vol.m_frag->getGrShader(), m_vol.m_prog);
 
 	SamplerInitInfo sinit;
 	sinit.m_repeat = false;
 	sinit.m_mipmapFilter = SamplingFilter::NEAREST;
-
-	ResourceGroupInitInfo rcinit;
-	rcinit.m_textures[0].m_texture = m_r->getDepthDownscale().m_hd.m_depthRt;
-	rcinit.m_textures[0].m_sampler = getGrManager().newInstance<Sampler>(sinit);
-	rcinit.m_textures[1].m_texture = m_r->getDepthDownscale().m_qd.m_depthRt;
-	rcinit.m_textures[1].m_sampler = rcinit.m_textures[0].m_sampler;
-	rcinit.m_textures[2].m_texture = m_r->getVolumetric().m_rt;
-	rcinit.m_uniformBuffers[0].m_uploadedMemory = true;
-	rcinit.m_uniformBuffers[0].m_usage = BufferUsageBit::UNIFORM_FRAGMENT;
-	m_vol.m_rc = getGrManager().newInstance<ResourceGroup>(rcinit);
+	m_vol.m_nearestSampler = getGrManager().newInstance<Sampler>(sinit);
 
 	return ErrorCode::NONE;
 }
@@ -128,16 +86,28 @@ void Fs::drawVolumetric(RenderingContext& ctx, CommandBufferPtr cmdb)
 {
 	const Frustum& fr = ctx.m_frustumComponent->getFrustum();
 
-	cmdb->bindPipeline(m_vol.m_ppline);
+	cmdb->bindShaderProgram(m_vol.m_prog);
+	cmdb->setBlendMethods(0, BlendMethod::ONE, BlendMethod::ONE);
+	cmdb->setDepthWrite(false);
+	cmdb->setDepthCompareFunction(CompareOperation::ALWAYS);
 
-	TransientMemoryInfo trans;
-	Vec4* unis = static_cast<Vec4*>(getGrManager().allocateFrameTransientMemory(
-		sizeof(Vec4), BufferUsageBit::UNIFORM_ALL, trans.m_uniformBuffers[0]));
+	TransientMemoryToken token;
+	Vec4* unis = static_cast<Vec4*>(
+		getGrManager().allocateFrameTransientMemory(sizeof(Vec4), BufferUsageBit::UNIFORM_ALL, token));
 	computeLinearizeDepthOptimal(fr.getNear(), fr.getFar(), unis->x(), unis->y());
 
-	cmdb->bindResourceGroup(m_vol.m_rc, 0, &trans);
+	cmdb->bindTextureAndSampler(0, 0, m_r->getDepthDownscale().m_hd.m_depthRt, m_vol.m_nearestSampler);
+	cmdb->bindTextureAndSampler(0, 1, m_r->getDepthDownscale().m_qd.m_depthRt, m_vol.m_nearestSampler);
+	cmdb->bindTexture(0, 2, m_r->getVolumetric().m_rt);
+
+	cmdb->bindUniformBuffer(0, 0, token);
 
 	m_r->drawQuad(cmdb);
+
+	// Restore state
+	cmdb->setBlendMethods(0, BlendMethod::ONE, BlendMethod::ZERO);
+	cmdb->setDepthWrite(true);
+	cmdb->setDepthCompareFunction(CompareOperation::LESS);
 }
 
 Error Fs::buildCommandBuffers(RenderingContext& ctx, U threadId, U threadCount) const
@@ -157,21 +127,28 @@ Error Fs::buildCommandBuffers(RenderingContext& ctx, U threadId, U threadCount)
 
 	// Create the command buffer and set some state
 	CommandBufferInitInfo cinf;
-	cinf.m_flags = CommandBufferFlag::SECOND_LEVEL;
+	cinf.m_flags = CommandBufferFlag::SECOND_LEVEL | CommandBufferFlag::GRAPHICS_WORK;
 	cinf.m_framebuffer = m_fb;
 	CommandBufferPtr cmdb = m_r->getGrManager().newInstance<CommandBuffer>(cinf);
 	ctx.m_fs.m_commandBuffers[threadId] = cmdb;
 
+	cmdb->bindTexture(1, 0, m_r->getDepthDownscale().m_hd.m_depthRt);
+	cmdb->bindTexture(1, 1, m_r->getSm().m_spotTexArray);
+	cmdb->bindTexture(1, 2, m_r->getSm().m_omniTexArray);
+	cmdb->bindUniformBuffer(1, 0, ctx.m_is.m_commonToken);
+	cmdb->bindUniformBuffer(1, 1, ctx.m_is.m_pointLightsToken);
+	cmdb->bindUniformBuffer(1, 2, ctx.m_is.m_spotLightsToken);
+	cmdb->bindStorageBuffer(1, 0, ctx.m_is.m_clustersToken);
+	cmdb->bindStorageBuffer(1, 1, ctx.m_is.m_lightIndicesToken);
+
 	cmdb->setViewport(0, 0, m_width, m_height);
-	cmdb->setPolygonOffset(0.0, 0.0);
-	cmdb->bindResourceGroup(m_globalResources, 1, &ctx.m_is.m_dynBufferInfo);
+	cmdb->setBlendMethods(0, BlendMethod::SRC_ALPHA, BlendMethod::ONE_MINUS_SRC_ALPHA);
+	cmdb->setDepthWrite(false);
 
 	// Start drawing
 	Error err = m_r->getSceneDrawer().drawRange(Pass::MS_FS,
 		*ctx.m_frustumComponent,
 		cmdb,
-		*m_pplineCache,
-		m_state,
 		vis.getBegin(VisibilityGroupType::RENDERABLES_FS) + start,
 		vis.getBegin(VisibilityGroupType::RENDERABLES_FS) + end);
 
@@ -199,7 +176,6 @@ void Fs::run(RenderingContext& ctx)
 	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
 	cmdb->beginRenderPass(m_fb);
 	cmdb->setViewport(0, 0, m_width, m_height);
-	cmdb->setPolygonOffset(0.0, 0.0);
 
 	for(U i = 0; i < m_r->getThreadPool().getThreadsCount(); ++i)
 	{

+ 3 - 5
src/anki/renderer/Fs.h

@@ -61,18 +61,16 @@ private:
 	U m_height;
 	FramebufferPtr m_fb;
 	TexturePtr m_rt;
-	ResourceGroupPtr m_globalResources;
-	PipelineInitInfo m_state;
-	GrObjectCache* m_pplineCache = nullptr;
 
 	class Vol
 	{
 	public:
 		ShaderResourcePtr m_frag;
-		PipelinePtr m_ppline;
-		ResourceGroupPtr m_rc;
+		ShaderProgramPtr m_prog;
+		SamplerPtr m_nearestSampler;
 	} m_vol;
 
+	ANKI_USE_RESULT Error initInternal(const ConfigSet& initializer);
 	ANKI_USE_RESULT Error initVol();
 };
 /// @}

+ 34 - 39
src/anki/renderer/FsUpscale.cpp

@@ -18,29 +18,25 @@ namespace anki
 
 Error FsUpscale::init(const ConfigSet& config)
 {
+	Error err = initInternal(config);
+	if(err)
+	{
+		ANKI_LOGE("Failed to initialize forward shading upscale");
+	}
+
+	return err;
+}
+
+Error FsUpscale::initInternal(const ConfigSet& config)
+{
+	ANKI_LOGI("Initializing forward shading upscale");
+
 	GrManager& gr = getGrManager();
 
-	// Create RC group
-	ResourceGroupInitInfo rcInit;
 	SamplerInitInfo sinit;
 	sinit.m_repeat = false;
-
-	rcInit.m_textures[0].m_texture = m_r->getMs().m_depthRt;
-	rcInit.m_textures[0].m_usage = TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ;
-
 	sinit.m_mipmapFilter = SamplingFilter::NEAREST;
-	rcInit.m_textures[1].m_texture = m_r->getDepthDownscale().m_hd.m_depthRt;
-	rcInit.m_textures[1].m_sampler = gr.newInstance<Sampler>(sinit);
-	rcInit.m_textures[1].m_usage = TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ;
-
-	rcInit.m_textures[2].m_texture = m_r->getFs().getRt();
-
-	rcInit.m_textures[3].m_texture = m_r->getSsao().getRt();
-
-	rcInit.m_uniformBuffers[0].m_uploadedMemory = true;
-	rcInit.m_uniformBuffers[0].m_usage = BufferUsageBit::UNIFORM_FRAGMENT;
-
-	m_rcGroup = getGrManager().newInstance<ResourceGroup>(rcInit);
+	m_nearestSampler = gr.newInstance<Sampler>(sinit);
 
 	// Shader
 	StringAuto pps(getFrameAllocator());
@@ -50,24 +46,12 @@ Error FsUpscale::init(const ConfigSet& config)
 		m_r->getHeight() / FS_FRACTION,
 		1);
 
-	ANKI_CHECK(getResourceManager().loadResourceToCache(m_frag, "shaders/FsUpscale.frag.glsl", pps.toCString(), "r_"));
-
-	ANKI_CHECK(getResourceManager().loadResourceToCache(m_vert, "shaders/Quad.vert.glsl", pps.toCString(), "r_"));
-
-	// Ppline
-	PipelineInitInfo ppinit;
-
-	ppinit.m_depthStencil.m_depthWriteEnabled = false;
-	ppinit.m_depthStencil.m_depthCompareFunction = CompareOperation::ALWAYS;
+	ANKI_CHECK(m_r->createShader("shaders/FsUpscale.frag.glsl", m_frag, pps.toCString()));
 
-	ppinit.m_color.m_attachmentCount = 1;
-	ppinit.m_color.m_attachments[0].m_format = IS_COLOR_ATTACHMENT_PIXEL_FORMAT;
-	ppinit.m_color.m_attachments[0].m_srcBlendMethod = BlendMethod::ONE;
-	ppinit.m_color.m_attachments[0].m_dstBlendMethod = BlendMethod::SRC_ALPHA;
+	ANKI_CHECK(m_r->createShader("shaders/Quad.vert.glsl", m_vert, pps.toCString()));
 
-	ppinit.m_shaders[U(ShaderType::VERTEX)] = m_vert->getGrShader();
-	ppinit.m_shaders[U(ShaderType::FRAGMENT)] = m_frag->getGrShader();
-	m_ppline = gr.newInstance<Pipeline>(ppinit);
+	// Prog
+	m_prog = gr.newInstance<ShaderProgram>(m_vert->getGrShader(), m_frag->getGrShader());
 
 	// Create FB
 	FramebufferInitInfo fbInit;
@@ -83,20 +67,31 @@ Error FsUpscale::init(const ConfigSet& config)
 void FsUpscale::run(RenderingContext& ctx)
 {
 	CommandBufferPtr cmdb = ctx.m_commandBuffer;
-	TransientMemoryInfo dyn;
 
-	Vec4* linearDepth = static_cast<Vec4*>(getGrManager().allocateFrameTransientMemory(
-		sizeof(Vec4), BufferUsageBit::UNIFORM_ALL, dyn.m_uniformBuffers[0]));
+	TransientMemoryToken token;
+
+	Vec4* linearDepth = static_cast<Vec4*>(
+		getGrManager().allocateFrameTransientMemory(sizeof(Vec4), BufferUsageBit::UNIFORM_ALL, token));
 	const Frustum& fr = ctx.m_frustumComponent->getFrustum();
 	computeLinearizeDepthOptimal(fr.getNear(), fr.getFar(), linearDepth->x(), linearDepth->y());
 
+	cmdb->bindUniformBuffer(0, 0, token);
+	cmdb->bindTexture(0, 0, m_r->getMs().m_depthRt);
+	cmdb->bindTextureAndSampler(0, 1, m_r->getDepthDownscale().m_hd.m_depthRt, m_nearestSampler);
+	cmdb->bindTexture(0, 2, m_r->getFs().getRt());
+	cmdb->bindTexture(0, 3, m_r->getSsao().getRt());
+
+	cmdb->setBlendMethods(0, BlendMethod::ONE, BlendMethod::SRC_ALPHA);
+
 	cmdb->beginRenderPass(m_fb);
-	cmdb->bindPipeline(m_ppline);
+	cmdb->bindShaderProgram(m_prog);
 	cmdb->setViewport(0, 0, m_r->getWidth(), m_r->getHeight());
-	cmdb->bindResourceGroup(m_rcGroup, 0, &dyn);
 
 	m_r->drawQuad(cmdb);
 	cmdb->endRenderPass();
+
+	// Restore state
+	cmdb->setBlendMethods(0, BlendMethod::ONE, BlendMethod::ZERO);
 }
 
 } // end namespace anki

+ 4 - 2
src/anki/renderer/FsUpscale.h

@@ -27,11 +27,13 @@ public:
 	void run(RenderingContext& ctx);
 
 private:
-	ResourceGroupPtr m_rcGroup;
 	FramebufferPtr m_fb;
 	ShaderResourcePtr m_frag;
 	ShaderResourcePtr m_vert;
-	PipelinePtr m_ppline;
+	ShaderProgramPtr m_prog;
+	SamplerPtr m_nearestSampler;
+
+	ANKI_USE_RESULT Error initInternal(const ConfigSet& config);
 };
 /// @}
 

+ 79 - 106
src/anki/renderer/Ir.cpp

@@ -57,7 +57,19 @@ Ir::~Ir()
 
 Error Ir::init(const ConfigSet& config)
 {
-	ANKI_LOGI("Initializing IR (Image Reflections)");
+	ANKI_LOGI("Initializing image reflections");
+
+	Error err = initInternal(config);
+	if(err)
+	{
+		ANKI_LOGE("Failed to initialize image reflections");
+	}
+
+	return err;
+}
+
+Error Ir::initInternal(const ConfigSet& config)
+{
 	m_fbSize = config.getNumber("ir.rendererSize");
 
 	if(m_fbSize < TILE_SIZE)
@@ -205,29 +217,6 @@ void Ir::initFaceInfo(U cacheEntryIdx, U faceIdx)
 
 	face.m_isFb = getGrManager().newInstance<Framebuffer>(fbInit);
 
-	// Create the IS resource group
-	ResourceGroupInitInfo rcinit;
-	rcinit.m_textures[0].m_texture = face.m_gbufferColorRts[0];
-	rcinit.m_textures[1].m_texture = face.m_gbufferColorRts[1];
-	rcinit.m_textures[2].m_texture = face.m_gbufferColorRts[2];
-	rcinit.m_textures[3].m_texture = face.m_gbufferDepthRt;
-	rcinit.m_textures[3].m_usage = TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ | TextureUsageBit::SAMPLED_FRAGMENT;
-
-	rcinit.m_uniformBuffers[0].m_uploadedMemory = true;
-	rcinit.m_uniformBuffers[0].m_usage = BufferUsageBit::UNIFORM_FRAGMENT | BufferUsageBit::UNIFORM_VERTEX;
-	rcinit.m_uniformBuffers[1].m_uploadedMemory = true;
-	rcinit.m_uniformBuffers[1].m_usage = BufferUsageBit::UNIFORM_FRAGMENT | BufferUsageBit::UNIFORM_VERTEX;
-
-	rcinit.m_vertexBuffers[0].m_buffer = m_is.m_plightPositions;
-	rcinit.m_indexBuffer.m_buffer = m_is.m_plightIndices;
-	rcinit.m_indexSize = 2;
-
-	face.m_plightRsrc = getGrManager().newInstance<ResourceGroup>(rcinit);
-
-	rcinit.m_vertexBuffers[0].m_buffer = m_is.m_slightPositions;
-	rcinit.m_indexBuffer.m_buffer = m_is.m_slightIndices;
-	face.m_slightRsrc = getGrManager().newInstance<ResourceGroup>(rcinit);
-
 	// Create irradiance FB
 	fbInit = FramebufferInitInfo();
 	fbInit.m_colorAttachmentCount = 1;
@@ -287,52 +276,28 @@ Error Ir::initIs()
 	// Init shaders
 	ANKI_CHECK(getResourceManager().loadResource("shaders/Light.vert.glsl", m_is.m_lightVert));
 
-	StringAuto pps(getAllocator());
-	pps.sprintf("#define POINT_LIGHT\n"
-				"#define RENDERING_WIDTH %d\n"
-				"#define RENDERING_HEIGHT %d\n",
+	ANKI_CHECK(m_r->createShaderf("shaders/Light.frag.glsl",
+		m_is.m_plightFrag,
+		"#define POINT_LIGHT\n"
+		"#define RENDERING_WIDTH %d\n"
+		"#define RENDERING_HEIGHT %d\n",
 		m_fbSize,
-		m_fbSize);
+		m_fbSize));
 
-	ANKI_CHECK(
-		getResourceManager().loadResourceToCache(m_is.m_plightFrag, "shaders/Light.frag.glsl", pps.toCString(), "r_"));
-
-	pps.destroy();
-	pps.sprintf("#define SPOT_LIGHT\n"
-				"#define RENDERING_WIDTH %d\n"
-				"#define RENDERING_HEIGHT %d\n",
+	ANKI_CHECK(m_r->createShaderf("shaders/Light.frag.glsl",
+		m_is.m_slightFrag,
+		"#define SPOT_LIGHT\n"
+		"#define RENDERING_WIDTH %d\n"
+		"#define RENDERING_HEIGHT %d\n",
 		m_fbSize,
-		m_fbSize);
-
-	ANKI_CHECK(
-		getResourceManager().loadResourceToCache(m_is.m_slightFrag, "shaders/Light.frag.glsl", pps.toCString(), "r_"));
-
-	// Init the pplines
-	PipelineInitInfo pinit;
-	pinit.m_vertex.m_attributeCount = 1;
-	pinit.m_vertex.m_attributes[0].m_format = PixelFormat(ComponentFormat::R32G32B32, TransformFormat::FLOAT);
-	pinit.m_vertex.m_bindingCount = 1;
-	pinit.m_vertex.m_bindings[0].m_stride = sizeof(F32) * 3;
-
-	pinit.m_rasterizer.m_cullMode = FaceSelectionMask::FRONT;
-
-	pinit.m_depthStencil.m_depthWriteEnabled = false;
-	pinit.m_depthStencil.m_depthCompareFunction = CompareOperation::GREATER;
-	pinit.m_depthStencil.m_format = MS_DEPTH_ATTACHMENT_PIXEL_FORMAT;
+		m_fbSize));
 
-	pinit.m_color.m_attachmentCount = 1;
-	auto& att = pinit.m_color.m_attachments[0];
-	att.m_format = IS_COLOR_ATTACHMENT_PIXEL_FORMAT;
-	att.m_srcBlendMethod = BlendMethod::ONE;
-	att.m_dstBlendMethod = BlendMethod::ONE;
+	// Init the progs
+	m_is.m_plightProg =
+		getGrManager().newInstance<ShaderProgram>(m_is.m_lightVert->getGrShader(), m_is.m_plightFrag->getGrShader());
 
-	pinit.m_shaders[ShaderType::VERTEX] = m_is.m_lightVert->getGrShader();
-	pinit.m_shaders[ShaderType::FRAGMENT] = m_is.m_plightFrag->getGrShader();
-
-	m_is.m_plightPpline = getGrManager().newInstance<Pipeline>(pinit);
-
-	pinit.m_shaders[ShaderType::FRAGMENT] = m_is.m_slightFrag->getGrShader();
-	m_is.m_slightPpline = getGrManager().newInstance<Pipeline>(pinit);
+	m_is.m_slightProg =
+		getGrManager().newInstance<ShaderProgram>(m_is.m_lightVert->getGrShader(), m_is.m_slightFrag->getGrShader());
 
 	// Init vert/idx buffers
 	ANKI_CHECK(
@@ -366,26 +331,11 @@ Error Ir::initIrradiance()
 	m_irradiance.m_cubeArr = getGrManager().newInstance<Texture>(texinit);
 
 	// Create the shader
-	StringAuto pps(getFrameAllocator());
-	pps.sprintf("#define CUBEMAP_SIZE %u\n", IRRADIANCE_TEX_SIZE);
-
-	ANKI_CHECK(getResourceManager().loadResourceToCache(
-		m_irradiance.m_frag, "shaders/Irradiance.frag.glsl", pps.toCString(), "r_"));
-
-	// Create the ppline
-	ColorStateInfo colorInf;
-	colorInf.m_attachmentCount = 1;
-	colorInf.m_attachments[0].m_format = IS_COLOR_ATTACHMENT_PIXEL_FORMAT;
+	ANKI_CHECK(m_r->createShaderf(
+		"shaders/Irradiance.frag.glsl", m_irradiance.m_frag, "#define CUBEMAP_SIZE %u\n", IRRADIANCE_TEX_SIZE));
 
-	m_r->createDrawQuadPipeline(m_irradiance.m_frag->getGrShader(), colorInf, m_irradiance.m_ppline);
-
-	// Create the resources
-	ResourceGroupInitInfo rcInit;
-	rcInit.m_uniformBuffers[0].m_uploadedMemory = true;
-	rcInit.m_uniformBuffers[0].m_usage = BufferUsageBit::UNIFORM_FRAGMENT | BufferUsageBit::UNIFORM_VERTEX;
-	rcInit.m_textures[0].m_texture = m_is.m_lightRt;
-
-	m_irradiance.m_rsrc = getGrManager().newInstance<ResourceGroup>(rcInit);
+	// Create the prog
+	m_r->createDrawQuadShaderProgram(m_irradiance.m_frag->getGrShader(), m_irradiance.m_prog);
 
 	// Clear texture
 	CommandBufferInitInfo cinf;
@@ -443,14 +393,11 @@ Error Ir::runMs(RenderingContext& rctx, FrustumComponent& frc, U layer, U faceId
 	// Start render pass
 	cmdb->beginRenderPass(face.m_msFb);
 	cmdb->setViewport(0, 0, m_fbSize, m_fbSize);
-	cmdb->setPolygonOffset(0.0, 0.0);
 
 	/// Draw
 	ANKI_CHECK(m_r->getSceneDrawer().drawRange(Pass::MS_FS,
 		frc,
 		cmdb,
-		*m_r->getMs().m_pplineCache,
-		m_r->getMs().m_state,
 		vis.getBegin(VisibilityGroupType::RENDERABLES_MS),
 		vis.getEnd(VisibilityGroupType::RENDERABLES_MS)));
 
@@ -485,13 +432,28 @@ void Ir::runIs(RenderingContext& rctx, FrustumComponent& frc, U layer, U faceIdx
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 		TextureSurfaceInfo(0, 0, faceIdx, layer));
 
+	// Set state
 	cmdb->beginRenderPass(face.m_isFb);
 
+	cmdb->bindTexture(0, 0, face.m_gbufferColorRts[0]);
+	cmdb->bindTexture(0, 1, face.m_gbufferColorRts[1]);
+	cmdb->bindTexture(0, 2, face.m_gbufferColorRts[2]);
+	cmdb->bindTexture(0, 3, face.m_gbufferDepthRt);
+
+	cmdb->setVertexAttribute(0, 0, PixelFormat(ComponentFormat::R32G32B32, TransformFormat::FLOAT), 0);
+
+	cmdb->setBlendMethods(0, BlendMethod::ONE, BlendMethod::ONE);
+	cmdb->setDepthCompareFunction(CompareOperation::GREATER);
+	cmdb->setDepthWrite(false);
+	cmdb->setCullMode(FaceSelectionMask::FRONT);
+
 	// Process all lights
 	const Mat4& vpMat = frc.getViewProjectionMatrix();
 	const Mat4& vMat = frc.getViewMatrix();
 
-	cmdb->bindPipeline(m_is.m_plightPpline);
+	cmdb->bindShaderProgram(m_is.m_plightProg);
+	cmdb->bindVertexBuffer(0, m_is.m_plightPositions, 0, sizeof(F32) * 3);
+	cmdb->bindIndexBuffer(m_is.m_plightIndices, 0, IndexType::U16);
 
 	const VisibleNode* it = vis.getBegin(VisibilityGroupType::LIGHTS_POINT);
 	const VisibleNode* end = vis.getEnd(VisibilityGroupType::LIGHTS_POINT);
@@ -500,11 +462,11 @@ void Ir::runIs(RenderingContext& rctx, FrustumComponent& frc, U layer, U faceIdx
 		const LightComponent& lightc = it->m_node->getComponent<LightComponent>();
 		const MoveComponent& movec = it->m_node->getComponent<MoveComponent>();
 
-		TransientMemoryInfo transient;
+		TransientMemoryToken uniVertToken, uniFragToken;
 
 		// Update uniforms
-		IrVertex* vert = static_cast<IrVertex*>(getGrManager().allocateFrameTransientMemory(
-			sizeof(IrVertex), BufferUsageBit::UNIFORM_ALL, transient.m_uniformBuffers[0]));
+		IrVertex* vert = static_cast<IrVertex*>(
+			getGrManager().allocateFrameTransientMemory(sizeof(IrVertex), BufferUsageBit::UNIFORM_ALL, uniVertToken));
 
 		Mat4 modelM(movec.getWorldTransform().getOrigin().xyz1(),
 			movec.getWorldTransform().getRotation().getRotationPart(),
@@ -513,7 +475,7 @@ void Ir::runIs(RenderingContext& rctx, FrustumComponent& frc, U layer, U faceIdx
 		vert->m_mvp = vpMat * modelM;
 
 		IrPointLight* light = static_cast<IrPointLight*>(getGrManager().allocateFrameTransientMemory(
-			sizeof(IrPointLight), BufferUsageBit::UNIFORM_ALL, transient.m_uniformBuffers[1]));
+			sizeof(IrPointLight), BufferUsageBit::UNIFORM_ALL, uniFragToken));
 
 		Vec4 pos = vMat * movec.getWorldTransform().getOrigin().xyz1();
 
@@ -523,15 +485,18 @@ void Ir::runIs(RenderingContext& rctx, FrustumComponent& frc, U layer, U faceIdx
 		light->m_specularColorPad1 = lightc.getSpecularColor();
 
 		// Bind stuff
-		cmdb->bindResourceGroup(face.m_plightRsrc, 0, &transient);
+		cmdb->bindUniformBuffer(0, 0, uniVertToken);
+		cmdb->bindUniformBuffer(0, 1, uniFragToken);
 
 		// Draw
-		cmdb->drawElements(m_is.m_plightIdxCount);
+		cmdb->drawElements(PrimitiveTopology::TRIANGLES, m_is.m_plightIdxCount);
 
 		++it;
 	}
 
-	cmdb->bindPipeline(m_is.m_slightPpline);
+	cmdb->bindShaderProgram(m_is.m_slightProg);
+	cmdb->bindVertexBuffer(0, m_is.m_slightPositions, 0, sizeof(F32) * 3);
+	cmdb->bindIndexBuffer(m_is.m_slightIndices, 0, IndexType::U16);
 
 	it = vis.getBegin(VisibilityGroupType::LIGHTS_SPOT);
 	end = vis.getEnd(VisibilityGroupType::LIGHTS_SPOT);
@@ -554,17 +519,17 @@ void Ir::runIs(RenderingContext& rctx, FrustumComponent& frc, U layer, U faceIdx
 
 		modelM = modelM * scaleM;
 
-		TransientMemoryInfo transient;
+		TransientMemoryToken uniVertToken, uniFragToken;
 
 		// Update vertex uniforms
-		IrVertex* vert = static_cast<IrVertex*>(getGrManager().allocateFrameTransientMemory(
-			sizeof(IrVertex), BufferUsageBit::UNIFORM_ALL, transient.m_uniformBuffers[0]));
+		IrVertex* vert = static_cast<IrVertex*>(
+			getGrManager().allocateFrameTransientMemory(sizeof(IrVertex), BufferUsageBit::UNIFORM_ALL, uniVertToken));
 
 		vert->m_mvp = vpMat * modelM;
 
 		// Update fragment uniforms
 		IrSpotLight* light = static_cast<IrSpotLight*>(getGrManager().allocateFrameTransientMemory(
-			sizeof(IrSpotLight), BufferUsageBit::UNIFORM_ALL, transient.m_uniformBuffers[1]));
+			sizeof(IrSpotLight), BufferUsageBit::UNIFORM_ALL, uniFragToken));
 
 		light->m_projectionParams = frc.getProjectionParameters();
 
@@ -580,10 +545,11 @@ void Ir::runIs(RenderingContext& rctx, FrustumComponent& frc, U layer, U faceIdx
 		light->m_lightDirPad1 = lightDir.xyz0();
 
 		// Bind stuff
-		cmdb->bindResourceGroup(face.m_slightRsrc, 0, &transient);
+		cmdb->bindUniformBuffer(0, 0, uniVertToken);
+		cmdb->bindUniformBuffer(0, 1, uniFragToken);
 
 		// Draw
-		cmdb->drawElements(m_is.m_slightIdxCount);
+		cmdb->drawElements(PrimitiveTopology::TRIANGLES, m_is.m_slightIdxCount);
 
 		++it;
 	}
@@ -602,6 +568,12 @@ void Ir::runIs(RenderingContext& rctx, FrustumComponent& frc, U layer, U faceIdx
 		TextureUsageBit::GENERATE_MIPMAPS,
 		TextureUsageBit::SAMPLED_FRAGMENT,
 		TextureSurfaceInfo(0, 0, faceIdx, layer));
+
+	// Restore state
+	cmdb->setBlendMethods(0, BlendMethod::ONE, BlendMethod::ZERO);
+	cmdb->setDepthCompareFunction(CompareOperation::LESS);
+	cmdb->setDepthWrite(true);
+	cmdb->setCullMode(FaceSelectionMask::BACK);
 }
 
 void Ir::computeIrradiance(RenderingContext& rctx, U layer, U faceIdx)
@@ -618,14 +590,15 @@ void Ir::computeIrradiance(RenderingContext& rctx, U layer, U faceIdx)
 	// Set state and draw
 	cmdb->setViewport(0, 0, IRRADIANCE_TEX_SIZE, IRRADIANCE_TEX_SIZE);
 
-	TransientMemoryInfo dinf;
-	UVec4* faceIdxArrayIdx = static_cast<UVec4*>(getGrManager().allocateFrameTransientMemory(
-		sizeof(UVec4), BufferUsageBit::UNIFORM_ALL, dinf.m_uniformBuffers[0]));
+	TransientMemoryToken token;
+	UVec4* faceIdxArrayIdx = static_cast<UVec4*>(
+		getGrManager().allocateFrameTransientMemory(sizeof(UVec4), BufferUsageBit::UNIFORM_ALL, token));
 	faceIdxArrayIdx->x() = faceIdx;
 	faceIdxArrayIdx->y() = layer;
 
-	cmdb->bindResourceGroup(m_irradiance.m_rsrc, 0, &dinf);
-	cmdb->bindPipeline(m_irradiance.m_ppline);
+	cmdb->bindUniformBuffer(0, 0, token);
+	cmdb->bindTexture(0, 0, m_is.m_lightRt);
+	cmdb->bindShaderProgram(m_irradiance.m_prog);
 	cmdb->beginRenderPass(face.m_irradianceFb);
 
 	m_r->drawQuad(cmdb);

+ 5 - 7
src/anki/renderer/Ir.h

@@ -32,7 +32,7 @@ anki_internal:
 
 	~Ir();
 
-	ANKI_USE_RESULT Error init(const ConfigSet& initializer);
+	ANKI_USE_RESULT Error init(const ConfigSet& cfg);
 
 	ANKI_USE_RESULT Error run(RenderingContext& ctx);
 
@@ -72,8 +72,6 @@ private:
 
 		// IS
 		FramebufferPtr m_isFb;
-		ResourceGroupPtr m_plightRsrc;
-		ResourceGroupPtr m_slightRsrc;
 
 		// Irradiance
 		FramebufferPtr m_irradianceFb;
@@ -110,8 +108,8 @@ private:
 		ShaderResourcePtr m_lightVert;
 		ShaderResourcePtr m_plightFrag;
 		ShaderResourcePtr m_slightFrag;
-		PipelinePtr m_plightPpline;
-		PipelinePtr m_slightPpline;
+		ShaderProgramPtr m_plightProg;
+		ShaderProgramPtr m_slightProg;
 
 		BufferPtr m_plightPositions;
 		BufferPtr m_plightIndices;
@@ -129,8 +127,7 @@ private:
 		U32 m_cubeArrMipCount = 0;
 
 		ShaderResourcePtr m_frag;
-		PipelinePtr m_ppline;
-		ResourceGroupPtr m_rsrc;
+		ShaderProgramPtr m_prog;
 	} m_irradiance;
 
 	DynamicArray<CacheEntry> m_cacheEntries;
@@ -140,6 +137,7 @@ private:
 	SamplerPtr m_integrationLutSampler;
 
 	// Init
+	ANKI_USE_RESULT Error initInternal(const ConfigSet& cfg);
 	ANKI_USE_RESULT Error initIs();
 	ANKI_USE_RESULT Error initIrradiance();
 	void initFaceInfo(U cacheEntryIdx, U faceIdx);

+ 68 - 138
src/anki/renderer/Is.cpp

@@ -6,7 +6,6 @@
 #include <anki/renderer/Is.h>
 #include <anki/renderer/Renderer.h>
 #include <anki/renderer/Sm.h>
-#include <anki/renderer/Pps.h>
 #include <anki/renderer/Ir.h>
 #include <anki/renderer/Ms.h>
 #include <anki/renderer/LightBin.h>
@@ -54,11 +53,12 @@ Is::~Is()
 
 Error Is::init(const ConfigSet& config)
 {
+	ANKI_LOGI("Initializing light stage");
 	Error err = initInternal(config);
 
 	if(err)
 	{
-		ANKI_LOGE("Failed to init IS");
+		ANKI_LOGE("Failed to init light stage");
 	}
 
 	return err;
@@ -89,11 +89,11 @@ Error Is::initInternal(const ConfigSet& config)
 		&getGrManager());
 
 	//
-	// Load the programs
+	// Load shaders and programs
 	//
 	StringAuto pps(getAllocator());
 
-	pps.sprintf("\n#define TILE_COUNT_X %u\n"
+	pps.sprintf("#define TILE_COUNT_X %u\n"
 				"#define TILE_COUNT_Y %u\n"
 				"#define CLUSTER_COUNT %u\n"
 				"#define RENDERER_WIDTH %u\n"
@@ -108,25 +108,14 @@ Error Is::initInternal(const ConfigSet& config)
 		m_r->getWidth(),
 		m_r->getHeight(),
 		m_maxLightIds,
-		m_r->getSm().getPoissonEnabled(),
+		m_r->getSm().m_poissonEnabled,
 		1,
 		m_r->getIr().getReflectionTextureMipmapCount());
 
-	// point light
-	ANKI_CHECK(getResourceManager().loadResourceToCache(m_lightVert, "shaders/Is.vert.glsl", pps.toCString(), "r_"));
+	ANKI_CHECK(m_r->createShader("shaders/Is.vert.glsl", m_lightVert, &pps[0]));
+	ANKI_CHECK(m_r->createShader("shaders/Is.frag.glsl", m_lightFrag, &pps[0]));
 
-	ANKI_CHECK(getResourceManager().loadResourceToCache(m_lightFrag, "shaders/Is.frag.glsl", pps.toCString(), "r_"));
-
-	PipelineInitInfo init;
-
-	init.m_inputAssembler.m_topology = PrimitiveTopology::TRIANGLE_STRIP;
-	init.m_depthStencil.m_depthWriteEnabled = false;
-	init.m_depthStencil.m_depthCompareFunction = CompareOperation::ALWAYS;
-	init.m_color.m_attachmentCount = 1;
-	init.m_color.m_attachments[0].m_format = IS_COLOR_ATTACHMENT_PIXEL_FORMAT;
-	init.m_shaders[U(ShaderType::VERTEX)] = m_lightVert->getGrShader();
-	init.m_shaders[U(ShaderType::FRAGMENT)] = m_lightFrag->getGrShader();
-	m_lightPpline = getGrManager().newInstance<Pipeline>(init);
+	m_lightProg = getGrManager().newInstance<ShaderProgram>(m_lightVert->getGrShader(), m_lightFrag->getGrShader());
 
 	//
 	// Create framebuffer
@@ -147,44 +136,6 @@ Error Is::initInternal(const ConfigSet& config)
 	fbInit.m_colorAttachments[0].m_usageInsideRenderPass = TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE;
 	m_fb = getGrManager().newInstance<Framebuffer>(fbInit);
 
-	//
-	// Create resource group
-	//
-	{
-		ResourceGroupInitInfo init;
-		init.m_textures[0].m_texture = m_r->getMs().m_rt0;
-		init.m_textures[1].m_texture = m_r->getMs().m_rt1;
-		init.m_textures[2].m_texture = m_r->getMs().m_rt2;
-		init.m_textures[3].m_texture = m_r->getMs().m_depthRt;
-		init.m_textures[3].m_usage = TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ;
-		init.m_textures[4].m_texture = m_r->getSm().getSpotTextureArray();
-		init.m_textures[5].m_texture = m_r->getSm().getOmniTextureArray();
-
-		init.m_textures[6].m_texture = m_r->getIr().getReflectionTexture();
-		init.m_textures[7].m_texture = m_r->getIr().getIrradianceTexture();
-
-		init.m_textures[8].m_texture = m_r->getIr().getIntegrationLut();
-		init.m_textures[8].m_sampler = m_r->getIr().getIntegrationLutSampler();
-
-		init.m_uniformBuffers[0].m_uploadedMemory = true;
-		init.m_uniformBuffers[0].m_usage = BufferUsageBit::UNIFORM_FRAGMENT | BufferUsageBit::UNIFORM_VERTEX;
-		init.m_uniformBuffers[1].m_uploadedMemory = true;
-		init.m_uniformBuffers[1].m_usage = BufferUsageBit::UNIFORM_FRAGMENT | BufferUsageBit::UNIFORM_VERTEX;
-		init.m_uniformBuffers[2].m_uploadedMemory = true;
-		init.m_uniformBuffers[2].m_usage = BufferUsageBit::UNIFORM_FRAGMENT | BufferUsageBit::UNIFORM_VERTEX;
-		init.m_uniformBuffers[3].m_uploadedMemory = true;
-		init.m_uniformBuffers[3].m_usage = BufferUsageBit::UNIFORM_FRAGMENT | BufferUsageBit::UNIFORM_VERTEX;
-		init.m_uniformBuffers[4].m_uploadedMemory = true;
-		init.m_uniformBuffers[4].m_usage = BufferUsageBit::UNIFORM_FRAGMENT | BufferUsageBit::UNIFORM_VERTEX;
-
-		init.m_storageBuffers[0].m_uploadedMemory = true;
-		init.m_storageBuffers[0].m_usage = BufferUsageBit::STORAGE_FRAGMENT_READ | BufferUsageBit::STORAGE_VERTEX_READ;
-		init.m_storageBuffers[1].m_uploadedMemory = true;
-		init.m_storageBuffers[1].m_usage = BufferUsageBit::STORAGE_FRAGMENT_READ | BufferUsageBit::STORAGE_VERTEX_READ;
-
-		m_rcGroup = getGrManager().newInstance<ResourceGroup>(init);
-	}
-
 	TextureInitInfo texinit;
 	texinit.m_width = texinit.m_height = 4;
 	texinit.m_usage = TextureUsageBit::SAMPLED_FRAGMENT;
@@ -198,48 +149,18 @@ Error Is::binLights(RenderingContext& ctx)
 {
 	updateCommonBlock(ctx);
 
-	TexturePtr diffDecalTex, normRoughnessDecalTex;
-
 	ANKI_CHECK(m_lightBin->bin(*ctx.m_frustumComponent,
 		getFrameAllocator(),
 		m_maxLightIds,
 		true,
-		ctx.m_is.m_dynBufferInfo.m_uniformBuffers[P_LIGHTS_LOCATION],
-		ctx.m_is.m_dynBufferInfo.m_uniformBuffers[S_LIGHTS_LOCATION],
-		&ctx.m_is.m_dynBufferInfo.m_uniformBuffers[PROBES_LOCATION],
-		ctx.m_is.m_dynBufferInfo.m_uniformBuffers[DECALS_LOCATION],
-		ctx.m_is.m_dynBufferInfo.m_storageBuffers[CLUSTERS_LOCATION],
-		ctx.m_is.m_dynBufferInfo.m_storageBuffers[LIGHT_IDS_LOCATION],
-		diffDecalTex,
-		normRoughnessDecalTex));
-
-	ResourceGroupInitInfo rcinit;
-	if(diffDecalTex)
-	{
-		rcinit.m_textures[0].m_texture = diffDecalTex;
-	}
-	else
-	{
-		// Bind something because validation layers will complain
-		rcinit.m_textures[0].m_texture = m_dummyTex;
-	}
-
-	if(normRoughnessDecalTex)
-	{
-		rcinit.m_textures[1].m_texture = normRoughnessDecalTex;
-	}
-	else
-	{
-		rcinit.m_textures[1].m_texture = m_dummyTex;
-	}
-
-	U64 hash = rcinit.computeHash();
-	if(hash != m_rcGroup1Hash)
-	{
-		m_rcGroup1Hash = hash;
-
-		m_rcGroup1 = getGrManager().newInstance<ResourceGroup>(rcinit);
-	}
+		ctx.m_is.m_pointLightsToken,
+		ctx.m_is.m_spotLightsToken,
+		&ctx.m_is.m_probesToken,
+		ctx.m_is.m_decalsToken,
+		ctx.m_is.m_clustersToken,
+		ctx.m_is.m_lightIndicesToken,
+		ctx.m_is.m_diffDecalTex,
+		ctx.m_is.m_normRoughnessDecalTex));
 
 	return ErrorCode::NONE;
 }
@@ -250,20 +171,39 @@ void Is::run(RenderingContext& ctx)
 
 	cmdb->beginRenderPass(m_fb);
 	cmdb->setViewport(0, 0, m_r->getWidth(), m_r->getHeight());
-	cmdb->bindPipeline(m_lightPpline);
-	cmdb->bindResourceGroup(m_rcGroup, 0, &ctx.m_is.m_dynBufferInfo);
-	cmdb->bindResourceGroup(m_rcGroup1, 1, nullptr);
-	cmdb->drawArrays(4, m_r->getTileCount());
+	cmdb->bindShaderProgram(m_lightProg);
+
+	cmdb->bindTexture(0, 0, m_r->getMs().m_rt0);
+	cmdb->bindTexture(0, 1, m_r->getMs().m_rt1);
+	cmdb->bindTexture(0, 2, m_r->getMs().m_rt2);
+	cmdb->bindTexture(0, 3, m_r->getMs().m_depthRt, DepthStencilAspectMask::DEPTH);
+	cmdb->bindTexture(0, 4, m_r->getSm().m_spotTexArray);
+	cmdb->bindTexture(0, 5, m_r->getSm().m_omniTexArray);
+	cmdb->bindTexture(0, 6, m_r->getIr().getReflectionTexture());
+	cmdb->bindTexture(0, 7, m_r->getIr().getIrradianceTexture());
+	cmdb->bindTextureAndSampler(0, 8, m_r->getIr().getIntegrationLut(), m_r->getIr().getIntegrationLutSampler());
+
+	cmdb->bindTexture(1, 0, (ctx.m_is.m_diffDecalTex) ? ctx.m_is.m_diffDecalTex : m_dummyTex);
+	cmdb->bindTexture(1, 1, (ctx.m_is.m_normRoughnessDecalTex) ? ctx.m_is.m_normRoughnessDecalTex : m_dummyTex);
+
+	cmdb->bindUniformBuffer(0, 0, ctx.m_is.m_commonToken);
+	cmdb->bindUniformBuffer(0, 1, ctx.m_is.m_pointLightsToken);
+	cmdb->bindUniformBuffer(0, 2, ctx.m_is.m_spotLightsToken);
+	cmdb->bindUniformBuffer(0, 3, ctx.m_is.m_probesToken);
+	cmdb->bindUniformBuffer(0, 4, ctx.m_is.m_decalsToken);
+
+	cmdb->bindStorageBuffer(0, 0, ctx.m_is.m_clustersToken);
+	cmdb->bindStorageBuffer(0, 1, ctx.m_is.m_lightIndicesToken);
+
+	cmdb->drawArrays(PrimitiveTopology::TRIANGLE_STRIP, 4, m_r->getTileCount());
 	cmdb->endRenderPass();
 }
 
 void Is::updateCommonBlock(RenderingContext& ctx)
 {
 	const FrustumComponent& fr = *ctx.m_frustumComponent;
-	ShaderCommonUniforms* blk =
-		static_cast<ShaderCommonUniforms*>(getGrManager().allocateFrameTransientMemory(sizeof(ShaderCommonUniforms),
-			BufferUsageBit::UNIFORM_ALL,
-			ctx.m_is.m_dynBufferInfo.m_uniformBuffers[COMMON_VARS_LOCATION]));
+	ShaderCommonUniforms* blk = static_cast<ShaderCommonUniforms*>(getGrManager().allocateFrameTransientMemory(
+		sizeof(ShaderCommonUniforms), BufferUsageBit::UNIFORM_ALL, ctx.m_is.m_commonToken));
 
 	// Start writing
 	blk->m_projectionParams = fr.getProjectionParameters();
@@ -286,62 +226,52 @@ void Is::setPreRunBarriers(RenderingContext& ctx)
 		TextureSurfaceInfo(0, 0, 0, 0));
 }
 
-Error Is::getOrCreatePipeline(ShaderVariantBit variantMask, RenderingContext& ctx, PipelinePtr& ppline)
+Error Is::getOrCreateProgram(ShaderVariantBit variantMask, RenderingContext& ctx, ShaderProgramPtr& prog)
 {
 	auto it = m_shaderVariantMap.find(variantMask);
 	if(it != m_shaderVariantMap.getEnd())
 	{
-		ppline = it->m_lightPpline;
+		prog = it->m_lightProg;
 	}
 	else
 	{
-		StringAuto pps(ctx.m_tempAllocator);
-
-		pps.sprintf("#define TILE_COUNT_X %u\n"
-					"#define TILE_COUNT_Y %u\n"
-					"#define CLUSTER_COUNT %u\n"
-					"#define RENDERER_WIDTH %u\n"
-					"#define RENDERER_HEIGHT %u\n"
-					"#define MAX_LIGHT_INDICES %u\n"
-					"#define POISSON %u\n"
-					"#define INDIRECT_ENABLED %u\n"
-					"#define IR_MIPMAP_COUNT %u\n"
-					"#define POINT_LIGHTS_ENABLED %u\n"
-					"#define SPOT_LIGHTS_ENABLED %u\n"
-					"#define DECALS_ENABLED %u\n"
-					"#define POINT_LIGHTS_SHADOWS_ENABLED %u\n"
-					"#define SPOT_LIGHTS_SHADOWS_ENABLED %u\n",
+		ShaderVariant variant;
+
+		ANKI_CHECK(m_r->createShaderf("shaders/Is.frag.glsl",
+			variant.m_lightFrag,
+			"#define TILE_COUNT_X %u\n"
+			"#define TILE_COUNT_Y %u\n"
+			"#define CLUSTER_COUNT %u\n"
+			"#define RENDERER_WIDTH %u\n"
+			"#define RENDERER_HEIGHT %u\n"
+			"#define MAX_LIGHT_INDICES %u\n"
+			"#define POISSON %u\n"
+			"#define INDIRECT_ENABLED %u\n"
+			"#define IR_MIPMAP_COUNT %u\n"
+			"#define POINT_LIGHTS_ENABLED %u\n"
+			"#define SPOT_LIGHTS_ENABLED %u\n"
+			"#define DECALS_ENABLED %u\n"
+			"#define POINT_LIGHTS_SHADOWS_ENABLED %u\n"
+			"#define SPOT_LIGHTS_SHADOWS_ENABLED %u\n",
 			m_r->getTileCountXY().x(),
 			m_r->getTileCountXY().y(),
 			m_clusterCount,
 			m_r->getWidth(),
 			m_r->getHeight(),
 			m_maxLightIds,
-			m_r->getSm().getPoissonEnabled(),
+			m_r->getSm().m_poissonEnabled,
 			!!(variantMask & ShaderVariantBit::INDIRECT),
 			m_r->getIr().getReflectionTextureMipmapCount(),
 			!!(variantMask & ShaderVariantBit::P_LIGHTS),
 			!!(variantMask & ShaderVariantBit::S_LIGHTS),
 			!!(variantMask & ShaderVariantBit::DECALS),
 			!!(variantMask & ShaderVariantBit::P_LIGHTS_SHADOWS),
-			!!(variantMask & ShaderVariantBit::S_LIGHTS_SHADOWS));
-
-		ShaderVariant variant;
-		ANKI_CHECK(getResourceManager().loadResourceToCache(
-			variant.m_lightFrag, "shaders/Is.frag.glsl", pps.toCString(), "r_"));
-
-		PipelineInitInfo init;
+			!!(variantMask & ShaderVariantBit::S_LIGHTS_SHADOWS)));
 
-		init.m_inputAssembler.m_topology = PrimitiveTopology::TRIANGLE_STRIP;
-		init.m_depthStencil.m_depthWriteEnabled = false;
-		init.m_depthStencil.m_depthCompareFunction = CompareOperation::ALWAYS;
-		init.m_color.m_attachmentCount = 1;
-		init.m_color.m_attachments[0].m_format = IS_COLOR_ATTACHMENT_PIXEL_FORMAT;
-		init.m_shaders[U(ShaderType::VERTEX)] = m_lightVert->getGrShader();
-		init.m_shaders[U(ShaderType::FRAGMENT)] = m_lightFrag->getGrShader();
-		variant.m_lightPpline = getGrManager().newInstance<Pipeline>(init);
+		variant.m_lightProg =
+			getGrManager().newInstance<ShaderProgram>(m_lightVert->getGrShader(), variant.m_lightFrag->getGrShader());
 
-		ppline = variant.m_lightPpline;
+		prog = variant.m_lightProg;
 
 		m_shaderVariantMap.pushBack(getAllocator(), variantMask, variant);
 	}

+ 4 - 15
src/anki/renderer/Is.h

@@ -53,14 +53,6 @@ anki_internal:
 	}
 
 private:
-	static const U COMMON_VARS_LOCATION = 0;
-	static const U P_LIGHTS_LOCATION = 1;
-	static const U S_LIGHTS_LOCATION = 2;
-	static const U PROBES_LOCATION = 3;
-	static const U DECALS_LOCATION = 4;
-	static const U CLUSTERS_LOCATION = 0;
-	static const U LIGHT_IDS_LOCATION = 1;
-
 	/// The IS render target
 	TexturePtr m_rt;
 	U8 m_rtMipCount = 0;
@@ -69,22 +61,18 @@ private:
 	/// The IS FBO
 	FramebufferPtr m_fb;
 
-	ResourceGroupPtr m_rcGroup;
-	U64 m_rcGroup1Hash = 0;
-	ResourceGroupPtr m_rcGroup1;
-
 	TexturePtr m_dummyTex;
 
 	// Light shaders
 	ShaderResourcePtr m_lightVert;
 	ShaderResourcePtr m_lightFrag;
-	PipelinePtr m_lightPpline;
+	ShaderProgramPtr m_lightProg;
 
 	class ShaderVariant
 	{
 	public:
 		ShaderResourcePtr m_lightFrag;
-		PipelinePtr m_lightPpline;
+		ShaderProgramPtr m_lightProg;
 	};
 
 	using Key = ShaderVariantBit;
@@ -113,7 +101,8 @@ private:
 
 	void updateCommonBlock(RenderingContext& ctx);
 
-	ANKI_USE_RESULT Error getOrCreatePipeline(ShaderVariantBit variantMask, RenderingContext& ctx, PipelinePtr& ppline);
+	ANKI_USE_RESULT Error getOrCreateProgram(
+		ShaderVariantBit variantMask, RenderingContext& ctx, ShaderProgramPtr& prog);
 };
 /// @}
 

+ 52 - 80
src/anki/renderer/Lf.cpp

@@ -34,10 +34,12 @@ Lf::~Lf()
 
 Error Lf::init(const ConfigSet& config)
 {
+	ANKI_LOGI("Initializing lens flare pass");
+
 	Error err = initInternal(config);
 	if(err)
 	{
-		ANKI_LOGE("Failed to init lens flare pass");
+		ANKI_LOGE("Failed to initialize lens flare pass");
 	}
 
 	return err;
@@ -48,7 +50,6 @@ Error Lf::initInternal(const ConfigSet& config)
 	ANKI_CHECK(initSprite(config));
 	ANKI_CHECK(initOcclusion(config));
 
-	getGrManager().finish();
 	return ErrorCode::NONE;
 }
 
@@ -70,26 +71,12 @@ Error Lf::initSprite(const ConfigSet& config)
 
 	pps.sprintf("#define MAX_SPRITES %u\n", m_maxSprites);
 
-	ANKI_CHECK(
-		getResourceManager().loadResourceToCache(m_realVert, "shaders/LfSpritePass.vert.glsl", pps.toCString(), "r_"));
-
-	ANKI_CHECK(
-		getResourceManager().loadResourceToCache(m_realFrag, "shaders/LfSpritePass.frag.glsl", pps.toCString(), "r_"));
-
-	// Create ppline.
-	// Writes to IS with blending
-	PipelineInitInfo init;
-	init.m_inputAssembler.m_topology = PrimitiveTopology::TRIANGLE_STRIP;
-	init.m_depthStencil.m_depthWriteEnabled = false;
-	init.m_depthStencil.m_depthCompareFunction = CompareOperation::ALWAYS;
-	init.m_depthStencil.m_format = MS_DEPTH_ATTACHMENT_PIXEL_FORMAT;
-	init.m_color.m_attachmentCount = 1;
-	init.m_color.m_attachments[0].m_format = IS_COLOR_ATTACHMENT_PIXEL_FORMAT;
-	init.m_color.m_attachments[0].m_srcBlendMethod = BlendMethod::ONE;
-	init.m_color.m_attachments[0].m_dstBlendMethod = BlendMethod::ONE;
-	init.m_shaders[U(ShaderType::VERTEX)] = m_realVert->getGrShader();
-	init.m_shaders[U(ShaderType::FRAGMENT)] = m_realFrag->getGrShader();
-	m_realPpline = getGrManager().newInstance<Pipeline>(init);
+	ANKI_CHECK(m_r->createShader("shaders/LfSpritePass.vert.glsl", m_realVert, pps.toCString()));
+
+	ANKI_CHECK(m_r->createShader("shaders/LfSpritePass.frag.glsl", m_realFrag, pps.toCString()));
+
+	// Create prog.
+	m_realProg = getGrManager().newInstance<ShaderProgram>(m_realVert->getGrShader(), m_realFrag->getGrShader());
 
 	return ErrorCode::NONE;
 }
@@ -112,48 +99,9 @@ Error Lf::initOcclusion(const ConfigSet& config)
 	ANKI_CHECK(getResourceManager().loadResource("shaders/LfOcclusion.frag.glsl", m_occlusionFrag));
 	ANKI_CHECK(getResourceManager().loadResource("shaders/LfUpdateIndirectInfo.comp.glsl", m_writeIndirectBuffComp));
 
-	PipelineInitInfo ppinit;
-	ppinit.m_shaders[ShaderType::COMPUTE] = m_writeIndirectBuffComp->getGrShader();
-	m_updateIndirectBuffPpline = gr.newInstance<Pipeline>(ppinit);
-
-	ResourceGroupInitInfo rcinit;
-	rcinit.m_storageBuffers[0].m_buffer = m_queryResultBuff;
-	rcinit.m_storageBuffers[0].m_usage = BufferUsageBit::STORAGE_COMPUTE_READ;
-	rcinit.m_storageBuffers[1].m_buffer = m_indirectBuff;
-	rcinit.m_storageBuffers[1].m_usage = BufferUsageBit::STORAGE_COMPUTE_WRITE;
-	m_updateIndirectBuffRsrc = gr.newInstance<ResourceGroup>(rcinit);
-
-	// Create ppline
-	// - only position attribute
-	// - points
-	// - test depth no write
-	// - will run after MS
-	// - will not update color
-	PipelineInitInfo init;
-	init.m_vertex.m_bindingCount = 1;
-	init.m_vertex.m_bindings[0].m_stride = sizeof(Vec3);
-	init.m_vertex.m_attributeCount = 1;
-	init.m_vertex.m_attributes[0].m_format = PixelFormat(ComponentFormat::R32G32B32, TransformFormat::FLOAT);
-	init.m_inputAssembler.m_topology = PrimitiveTopology::POINTS;
-	init.m_depthStencil.m_depthWriteEnabled = false;
-	init.m_depthStencil.m_format = MS_DEPTH_ATTACHMENT_PIXEL_FORMAT;
-	ANKI_ASSERT(MS_COLOR_ATTACHMENT_COUNT == 3);
-	init.m_color.m_attachmentCount = MS_COLOR_ATTACHMENT_COUNT;
-	init.m_color.m_attachments[0].m_format = MS_COLOR_ATTACHMENT_PIXEL_FORMATS[0];
-	init.m_color.m_attachments[0].m_channelWriteMask = ColorBit::NONE;
-	init.m_color.m_attachments[1].m_format = MS_COLOR_ATTACHMENT_PIXEL_FORMATS[1];
-	init.m_color.m_attachments[1].m_channelWriteMask = ColorBit::NONE;
-	init.m_color.m_attachments[2].m_format = MS_COLOR_ATTACHMENT_PIXEL_FORMATS[2];
-	init.m_color.m_attachments[2].m_channelWriteMask = ColorBit::NONE;
-	init.m_shaders[ShaderType::VERTEX] = m_occlusionVert->getGrShader();
-	init.m_shaders[ShaderType::FRAGMENT] = m_occlusionFrag->getGrShader();
-	m_occlusionPpline = gr.newInstance<Pipeline>(init);
-
-	rcinit = ResourceGroupInitInfo();
-	rcinit.m_vertexBuffers[0].m_uploadedMemory = true;
-	rcinit.m_uniformBuffers[0].m_uploadedMemory = true;
-	rcinit.m_uniformBuffers[0].m_usage = BufferUsageBit::UNIFORM_FRAGMENT | BufferUsageBit::UNIFORM_VERTEX;
-	m_occlusionRcGroup = gr.newInstance<ResourceGroup>(rcinit);
+	m_updateIndirectBuffProg = gr.newInstance<ShaderProgram>(m_writeIndirectBuffComp->getGrShader());
+
+	m_occlusionProg = gr.newInstance<ShaderProgram>(m_occlusionVert->getGrShader(), m_occlusionFrag->getGrShader());
 
 	return ErrorCode::NONE;
 }
@@ -191,21 +139,27 @@ void Lf::runOcclusionTests(RenderingContext& ctx, CommandBufferPtr cmdb)
 	const Vec3* initialPositions;
 	if(count)
 	{
-		TransientMemoryInfo dyn;
+		TransientMemoryToken uniToken, vertToken;
 
 		// Setup MVP UBO
-		Mat4* mvp = static_cast<Mat4*>(getGrManager().allocateFrameTransientMemory(
-			sizeof(Mat4), BufferUsageBit::UNIFORM_ALL, dyn.m_uniformBuffers[0]));
+		Mat4* mvp = static_cast<Mat4*>(
+			getGrManager().allocateFrameTransientMemory(sizeof(Mat4), BufferUsageBit::UNIFORM_ALL, uniToken));
 		*mvp = camFr.getViewProjectionMatrix();
 
 		// Alloc dyn mem
-		positions = static_cast<Vec3*>(getGrManager().allocateFrameTransientMemory(
-			sizeof(Vec3) * count, BufferUsageBit::VERTEX, dyn.m_vertexBuffers[0]));
+		positions = static_cast<Vec3*>(
+			getGrManager().allocateFrameTransientMemory(sizeof(Vec3) * count, BufferUsageBit::VERTEX, vertToken));
 		initialPositions = positions;
 
 		// Setup state
-		cmdb->bindPipeline(m_occlusionPpline);
-		cmdb->bindResourceGroup(m_occlusionRcGroup, 0, &dyn);
+		cmdb->bindShaderProgram(m_occlusionProg);
+		cmdb->bindUniformBuffer(0, 0, uniToken);
+		cmdb->bindVertexBuffer(0, vertToken, sizeof(Vec3));
+		cmdb->setVertexAttribute(0, 0, PixelFormat(ComponentFormat::R32G32B32, TransformFormat::FLOAT), 0);
+		cmdb->setColorChannelWriteMask(0, ColorBit::NONE);
+		cmdb->setColorChannelWriteMask(1, ColorBit::NONE);
+		cmdb->setColorChannelWriteMask(2, ColorBit::NONE);
+		cmdb->setDepthWrite(false);
 	}
 
 	for(U i = 0; i < count; ++i)
@@ -218,11 +172,20 @@ void Lf::runOcclusionTests(RenderingContext& ctx, CommandBufferPtr cmdb)
 
 		// Draw and query
 		cmdb->beginOcclusionQuery(m_queries[i]);
-		cmdb->drawArrays(1, 1, positions - initialPositions);
+		cmdb->drawArrays(PrimitiveTopology::POINTS, 1, 1, positions - initialPositions);
 		cmdb->endOcclusionQuery(m_queries[i]);
 
 		++positions;
 	}
+
+	// Restore state
+	if(count)
+	{
+		cmdb->setColorChannelWriteMask(0, ColorBit::ALL);
+		cmdb->setColorChannelWriteMask(1, ColorBit::ALL);
+		cmdb->setColorChannelWriteMask(2, ColorBit::ALL);
+		cmdb->setDepthWrite(true);
+	}
 }
 
 void Lf::updateIndirectInfo(RenderingContext& ctx, CommandBufferPtr cmdb)
@@ -251,8 +214,9 @@ void Lf::updateIndirectInfo(RenderingContext& ctx, CommandBufferPtr cmdb)
 		sizeof(DrawArraysIndirectInfo) * count);
 
 	// Update the indirect info
-	cmdb->bindPipeline(m_updateIndirectBuffPpline);
-	cmdb->bindResourceGroup(m_updateIndirectBuffRsrc, 0, nullptr);
+	cmdb->bindShaderProgram(m_updateIndirectBuffProg);
+	cmdb->bindStorageBuffer(0, 0, m_queryResultBuff, 0);
+	cmdb->bindStorageBuffer(0, 1, m_indirectBuff, 0);
 	cmdb->dispatchCompute(count, 1, 1);
 
 	// Set barrier
@@ -269,13 +233,16 @@ void Lf::run(RenderingContext& ctx, CommandBufferPtr cmdb)
 	FrustumComponent& camFr = *ctx.m_frustumComponent;
 	VisibilityTestResults& vi = camFr.getVisibilityTestResults();
 
-	U count = min<U>(vi.getCount(VisibilityGroupType::FLARES), m_maxFlares);
+	const U count = min<U>(vi.getCount(VisibilityGroupType::FLARES), m_maxFlares);
 	if(count == 0)
 	{
 		return;
 	}
 
-	cmdb->bindPipeline(m_realPpline);
+	cmdb->bindShaderProgram(m_realProg);
+	cmdb->setDepthWrite(false);
+	cmdb->setDepthCompareFunction(CompareOperation::ALWAYS);
+
 	for(U i = 0; i < count; ++i)
 	{
 		auto it = vi.getBegin(VisibilityGroupType::FLARES) + i;
@@ -314,11 +281,16 @@ void Lf::run(RenderingContext& ctx, CommandBufferPtr cmdb)
 		++c;
 
 		// Render
-		TransientMemoryInfo dyn;
-		dyn.m_uniformBuffers[0] = token;
-		cmdb->bindResourceGroup(lf.getResourceGroup(), 0, &dyn);
-		cmdb->drawArraysIndirect(1, i * sizeof(DrawArraysIndirectInfo), m_indirectBuff);
+		cmdb->bindTexture(0, 0, lf.getTexture());
+		cmdb->bindUniformBuffer(0, 0, token);
+
+		cmdb->drawArraysIndirect(
+			PrimitiveTopology::TRIANGLE_STRIP, 1, i * sizeof(DrawArraysIndirectInfo), m_indirectBuff);
 	}
+
+	// Restore state
+	cmdb->setDepthWrite(true);
+	cmdb->setDepthCompareFunction(CompareOperation::LESS);
 }
 
 } // end namespace anki

+ 3 - 5
src/anki/renderer/Lf.h

@@ -43,18 +43,16 @@ private:
 	BufferPtr m_queryResultBuff;
 	BufferPtr m_indirectBuff;
 	ShaderResourcePtr m_writeIndirectBuffComp;
-	PipelinePtr m_updateIndirectBuffPpline;
-	ResourceGroupPtr m_updateIndirectBuffRsrc;
+	ShaderProgramPtr m_updateIndirectBuffProg;
 
 	ShaderResourcePtr m_occlusionVert;
 	ShaderResourcePtr m_occlusionFrag;
-	PipelinePtr m_occlusionPpline;
-	ResourceGroupPtr m_occlusionRcGroup;
+	ShaderProgramPtr m_occlusionProg;
 
 	// Sprite billboards
 	ShaderResourcePtr m_realVert;
 	ShaderResourcePtr m_realFrag;
-	PipelinePtr m_realPpline;
+	ShaderProgramPtr m_realProg;
 	U8 m_maxSpritesPerFlare;
 	U8 m_maxFlares;
 	U16 m_maxSprites;

+ 5 - 14
src/anki/renderer/MainRenderer.cpp

@@ -81,16 +81,8 @@ Error MainRenderer::create(ThreadPool* threadpool,
 	if(!m_rDrawToDefaultFb)
 	{
 		ANKI_CHECK(m_r->getResourceManager().loadResource("shaders/Final.frag.glsl", m_blitFrag));
+		m_r->createDrawQuadShaderProgram(m_blitFrag->getGrShader(), m_blitProg);
 
-		ColorStateInfo colorState;
-		colorState.m_attachmentCount = 1;
-		colorState.m_attachments[0].m_format.m_components = ComponentFormat::DEFAULT_FRAMEBUFFER;
-		m_r->createDrawQuadPipeline(m_blitFrag->getGrShader(), colorState, m_blitPpline);
-
-		// Init RC group
-		ResourceGroupInitInfo rcinit;
-		rcinit.m_textures[0].m_texture = m_r->getPps().getRt();
-		m_rcGroup = m_r->getGrManager().newInstance<ResourceGroup>(rcinit);
 		ANKI_LOGI("The main renderer will have to blit the offscreen renderer's result");
 	}
 
@@ -108,12 +100,11 @@ Error MainRenderer::render(SceneGraph& scene)
 
 	GrManager& gl = m_r->getGrManager();
 	CommandBufferInitInfo cinf;
+	cinf.m_flags =
+		CommandBufferFlag::COMPUTE_WORK | CommandBufferFlag::GRAPHICS_WORK | CommandBufferFlag::TRANSFER_WORK;
 	cinf.m_hints = m_cbInitHints;
 	CommandBufferPtr cmdb = gl.newInstance<CommandBuffer>(cinf);
 
-	// Set some of the dynamic state
-	cmdb->setPolygonOffset(0.0, 0.0);
-
 	// Run renderer
 	RenderingContext ctx(m_frameAlloc);
 
@@ -134,8 +125,8 @@ Error MainRenderer::render(SceneGraph& scene)
 		cmdb->beginRenderPass(m_defaultFb);
 		cmdb->setViewport(0, 0, m_width, m_height);
 
-		cmdb->bindPipeline(m_blitPpline);
-		cmdb->bindResourceGroup(m_rcGroup, 0, nullptr);
+		cmdb->bindShaderProgram(m_blitProg);
+		cmdb->bindTexture(0, 0, m_r->getPps().getRt());
 
 		m_r->drawQuad(cmdb);
 		cmdb->endRenderPass();

+ 1 - 2
src/anki/renderer/MainRenderer.h

@@ -67,8 +67,7 @@ private:
 	Bool8 m_rDrawToDefaultFb = false;
 
 	ShaderResourcePtr m_blitFrag;
-	PipelinePtr m_blitPpline;
-	ResourceGroupPtr m_rcGroup;
+	ShaderProgramPtr m_blitProg;
 
 	FramebufferPtr m_defaultFb;
 	U32 m_width = 0; ///< Default FB size.

+ 7 - 26
src/anki/renderer/Ms.cpp

@@ -17,10 +17,6 @@ namespace anki
 
 Ms::~Ms()
 {
-	if(m_pplineCache)
-	{
-		getAllocator().deleteInstance(m_pplineCache);
-	}
 }
 
 Error Ms::createRt(U32 samples)
@@ -91,10 +87,12 @@ Error Ms::createRt(U32 samples)
 
 Error Ms::init(const ConfigSet& initializer)
 {
+	ANKI_LOGI("Initializing g-buffer pass");
+
 	Error err = initInternal(initializer);
 	if(err)
 	{
-		ANKI_LOGE("Failed to initialize material stage");
+		ANKI_LOGE("Failed to initialize g-buffer pass");
 	}
 
 	return err;
@@ -103,21 +101,6 @@ Error Ms::init(const ConfigSet& initializer)
 Error Ms::initInternal(const ConfigSet& initializer)
 {
 	ANKI_CHECK(createRt(initializer.getNumber("samples")));
-
-	getGrManager().finish();
-
-	{
-		ColorStateInfo& color = m_state.m_color;
-		color.m_attachmentCount = MS_COLOR_ATTACHMENT_COUNT;
-		color.m_attachments[0].m_format = MS_COLOR_ATTACHMENT_PIXEL_FORMATS[0];
-		color.m_attachments[1].m_format = MS_COLOR_ATTACHMENT_PIXEL_FORMATS[1];
-		color.m_attachments[2].m_format = MS_COLOR_ATTACHMENT_PIXEL_FORMATS[2];
-
-		m_state.m_depthStencil.m_format = MS_DEPTH_ATTACHMENT_PIXEL_FORMAT;
-	}
-
-	m_pplineCache = getAllocator().newInstance<GrObjectCache>(&getGrManager());
-
 	return ErrorCode::NONE;
 }
 
@@ -134,21 +117,20 @@ Error Ms::buildCommandBuffers(RenderingContext& ctx, U threadId, U threadCount)
 
 	if(start != end)
 	{
-		// Create the command buffer and set some state
+		// Create the command buffer
 		CommandBufferInitInfo cinf;
-		cinf.m_flags = CommandBufferFlag::SECOND_LEVEL;
+		cinf.m_flags = CommandBufferFlag::SECOND_LEVEL | CommandBufferFlag::GRAPHICS_WORK;
 		cinf.m_framebuffer = m_fb;
 		CommandBufferPtr cmdb = m_r->getGrManager().newInstance<CommandBuffer>(cinf);
 		ctx.m_ms.m_commandBuffers[threadId] = cmdb;
+
+		// Set some state, leave the rest to default
 		cmdb->setViewport(0, 0, m_r->getWidth(), m_r->getHeight());
-		cmdb->setPolygonOffset(0.0, 0.0);
 
 		// Start drawing
 		ANKI_CHECK(m_r->getSceneDrawer().drawRange(Pass::MS_FS,
 			*ctx.m_frustumComponent,
 			cmdb,
-			*m_pplineCache,
-			m_state,
 			vis.getBegin(VisibilityGroupType::RENDERABLES_MS) + start,
 			vis.getBegin(VisibilityGroupType::RENDERABLES_MS) + end));
 	}
@@ -166,7 +148,6 @@ void Ms::run(RenderingContext& ctx)
 
 	// Set some state anyway because other stages may depend on it
 	cmdb->setViewport(0, 0, m_r->getWidth(), m_r->getHeight());
-	cmdb->setPolygonOffset(0.0, 0.0);
 
 	for(U i = 0; i < m_r->getThreadPool().getThreadsCount(); ++i)
 	{

+ 0 - 3
src/anki/renderer/Ms.h

@@ -23,9 +23,6 @@ anki_internal:
 	TexturePtr m_rt2;
 	TexturePtr m_depthRt;
 
-	PipelineInitInfo m_state;
-	GrObjectCache* m_pplineCache = nullptr;
-
 	Ms(Renderer* r)
 		: RenderingPass(r)
 	{

+ 32 - 65
src/anki/renderer/Pps.cpp

@@ -78,7 +78,6 @@ Error Pps::loadColorGradingTexture(CString filename)
 	ANKI_ASSERT(m_lut->getHeight() == LUT_SIZE);
 	ANKI_ASSERT(m_lut->getDepth() == LUT_SIZE);
 
-	m_lutDirty = true;
 	return ErrorCode::NONE;
 }
 
@@ -91,27 +90,27 @@ Error Pps::run(RenderingContext& ctx)
 	Bool dbgEnabled = m_r->getDbg().getEnabled();
 
 	// Get or create the ppline
-	PipelinePtr& ppline = m_ppline[drawToDefaultFb][dbgEnabled];
+	ShaderProgramPtr& prog = m_prog[drawToDefaultFb][dbgEnabled];
 
-	if(!ppline)
+	if(!prog)
 	{
 		// Need to create it
 
 		ShaderResourcePtr& frag = m_frag[drawToDefaultFb][dbgEnabled];
 		if(!frag)
 		{
-			StringAuto pps(ctx.m_tempAllocator);
-
-			pps.sprintf("#define BLOOM_ENABLED %u\n"
-						"#define SHARPEN_ENABLED %u\n"
-						"#define FBO_WIDTH %u\n"
-						"#define FBO_HEIGHT %u\n"
-						"#define LUT_SIZE %u.0\n"
-						"#define DBG_ENABLED %u\n"
-						"#define DRAW_TO_DEFAULT %u\n"
-						"#define SMAA_ENABLED 1\n"
-						"#define SMAA_RT_METRICS vec4(%f, %f, %f, %f)\n"
-						"#define SMAA_PRESET_%s\n",
+			ANKI_CHECK(m_r->createShaderf("shaders/Pps.frag.glsl",
+				frag,
+				"#define BLOOM_ENABLED %u\n"
+				"#define SHARPEN_ENABLED %u\n"
+				"#define FBO_WIDTH %u\n"
+				"#define FBO_HEIGHT %u\n"
+				"#define LUT_SIZE %u.0\n"
+				"#define DBG_ENABLED %u\n"
+				"#define DRAW_TO_DEFAULT %u\n"
+				"#define SMAA_ENABLED 1\n"
+				"#define SMAA_RT_METRICS vec4(%f, %f, %f, %f)\n"
+				"#define SMAA_PRESET_%s\n",
 				true,
 				m_sharpenEnabled,
 				m_r->getWidth(),
@@ -123,69 +122,38 @@ Error Pps::run(RenderingContext& ctx)
 				1.0 / m_r->getHeight(),
 				F32(m_r->getWidth()),
 				F32(m_r->getHeight()),
-				&m_r->getSmaa().m_qualityPerset[0]);
-
-			ANKI_CHECK(getResourceManager().loadResourceToCache(frag, "shaders/Pps.frag.glsl", pps.toCString(), "r_"));
+				&m_r->getSmaa().m_qualityPerset[0]));
 		}
 
 		if(!m_vert)
 		{
-			StringAuto pps(ctx.m_tempAllocator);
-
-			pps.sprintf("#define SMAA_ENABLED 1\n"
-						"#define SMAA_RT_METRICS vec4(%f, %f, %f, %f)\n"
-						"#define SMAA_PRESET_%s\n",
+			ANKI_CHECK(m_r->createShaderf("shaders/Pps.vert.glsl",
+				m_vert,
+				"#define SMAA_ENABLED 1\n"
+				"#define SMAA_RT_METRICS vec4(%f, %f, %f, %f)\n"
+				"#define SMAA_PRESET_%s\n",
 				1.0 / m_r->getWidth(),
 				1.0 / m_r->getHeight(),
 				F32(m_r->getWidth()),
 				F32(m_r->getHeight()),
-				&m_r->getSmaa().m_qualityPerset[0]);
-
-			ANKI_CHECK(
-				getResourceManager().loadResourceToCache(m_vert, "shaders/Pps.vert.glsl", pps.toCString(), "r_"));
+				&m_r->getSmaa().m_qualityPerset[0]));
 		}
 
-		PixelFormat pfs = (drawToDefaultFb) ? PixelFormat(ComponentFormat::DEFAULT_FRAMEBUFFER, TransformFormat::NONE)
-											: RT_PIXEL_FORMAT;
-
-		PipelineInitInfo ppinit;
-
-		ppinit.m_inputAssembler.m_topology = PrimitiveTopology::TRIANGLE_STRIP;
-
-		ppinit.m_depthStencil.m_depthWriteEnabled = false;
-		ppinit.m_depthStencil.m_depthCompareFunction = CompareOperation::ALWAYS;
-
-		ppinit.m_color.m_attachmentCount = 1;
-		ppinit.m_color.m_attachments[0].m_format = pfs;
-
-		ppinit.m_shaders[ShaderType::VERTEX] = m_vert->getGrShader();
-		ppinit.m_shaders[ShaderType::FRAGMENT] = frag->getGrShader();
-
-		ppline = m_r->getGrManager().newInstance<Pipeline>(ppinit);
+		prog = getGrManager().newInstance<ShaderProgram>(m_vert->getGrShader(), frag->getGrShader());
 	}
 
-	// Get or create the resource group
-	ResourceGroupPtr& rsrc = m_rcGroup[dbgEnabled];
-	if(!rsrc || m_lutDirty)
+	// Bind stuff
+	cmdb->bindTexture(0, 0, m_r->getIs().getRt());
+	cmdb->bindTexture(0, 1, m_r->getBloom().m_upscale.m_rt);
+	cmdb->bindTexture(0, 2, m_lut->getGrTexture());
+	cmdb->bindTexture(0, 3, m_r->getSmaa().m_weights.m_rt);
+	if(dbgEnabled)
 	{
-		ResourceGroupInitInfo rcInit;
-		rcInit.m_textures[0].m_texture = m_r->getIs().getRt();
-		rcInit.m_textures[1].m_texture = m_r->getBloom().m_upscale.m_rt;
-		rcInit.m_textures[2].m_texture = m_lut->getGrTexture();
-		rcInit.m_textures[3].m_texture = m_r->getSmaa().m_weights.m_rt;
-		if(dbgEnabled)
-		{
-			rcInit.m_textures[4].m_texture = m_r->getDbg().getRt();
-		}
-
-		rcInit.m_storageBuffers[0].m_buffer = m_r->getTm().getAverageLuminanceBuffer();
-		rcInit.m_storageBuffers[0].m_usage = BufferUsageBit::STORAGE_FRAGMENT_READ;
-
-		rsrc = getGrManager().newInstance<ResourceGroup>(rcInit);
-
-		m_lutDirty = false;
+		cmdb->bindTexture(0, 4, m_r->getDbg().getRt());
 	}
 
+	cmdb->bindStorageBuffer(0, 0, m_r->getTm().m_luminanceBuff, 0);
+
 	// Get or create FB
 	FramebufferPtr* fb = nullptr;
 	U width, height;
@@ -204,8 +172,7 @@ Error Pps::run(RenderingContext& ctx)
 
 	cmdb->beginRenderPass(*fb);
 	cmdb->setViewport(0, 0, width, height);
-	cmdb->bindPipeline(ppline);
-	cmdb->bindResourceGroup(rsrc, 0, nullptr);
+	cmdb->bindShaderProgram(prog);
 	m_r->drawQuad(cmdb);
 	cmdb->endRenderPass();
 

+ 1 - 3
src/anki/renderer/Pps.h

@@ -46,12 +46,10 @@ private:
 	FramebufferPtr m_fb;
 	Array2d<ShaderResourcePtr, 2, 2> m_frag; ///< One with Dbg and one without
 	ShaderResourcePtr m_vert;
-	Array2d<PipelinePtr, 2, 2> m_ppline; ///< With Dbg, Default FB or not
+	Array2d<ShaderProgramPtr, 2, 2> m_prog; ///< With Dbg, Default FB or not
 	TexturePtr m_rt;
-	Array<ResourceGroupPtr, 2> m_rcGroup; ///< One with Dbg and one without
 
 	TextureResourcePtr m_lut; ///< Color grading lookup texture.
-	Bool8 m_lutDirty = true;
 
 	Bool8 m_sharpenEnabled = false;
 

+ 12 - 20
src/anki/renderer/Renderer.cpp

@@ -20,7 +20,6 @@
 #include <anki/renderer/Fs.h>
 #include <anki/renderer/Lf.h>
 #include <anki/renderer/Dbg.h>
-#include <anki/renderer/Tiler.h>
 #include <anki/renderer/FsUpscale.h>
 #include <anki/renderer/DownscaleBlur.h>
 #include <anki/renderer/Volumetric.h>
@@ -165,7 +164,7 @@ Error Renderer::initInternal(const ConfigSet& config)
 	ANKI_CHECK(m_fsUpscale->init(config));
 
 	m_tm.reset(getAllocator().newInstance<Tm>(this));
-	ANKI_CHECK(m_tm->create(config));
+	ANKI_CHECK(m_tm->init(config));
 
 	m_downscale.reset(getAllocator().newInstance<DownscaleBlur>(this));
 	ANKI_CHECK(m_downscale->init(config));
@@ -372,22 +371,6 @@ void Renderer::clearRenderTarget(TexturePtr rt, const ClearValue& clear, Texture
 	cmdb->flush();
 }
 
-void Renderer::createDrawQuadPipeline(ShaderPtr frag, const ColorStateInfo& colorState, PipelinePtr& ppline)
-{
-	PipelineInitInfo init;
-
-	init.m_inputAssembler.m_topology = PrimitiveTopology::TRIANGLE_STRIP;
-
-	init.m_depthStencil.m_depthWriteEnabled = false;
-	init.m_depthStencil.m_depthCompareFunction = CompareOperation::ALWAYS;
-
-	init.m_color = colorState;
-
-	init.m_shaders[ShaderType::VERTEX] = m_drawQuadVert->getGrShader();
-	init.m_shaders[ShaderType::FRAGMENT] = frag;
-	ppline = m_gr->newInstance<Pipeline>(init);
-}
-
 Error Renderer::buildCommandBuffersInternal(RenderingContext& ctx, U32 threadId, PtrSize threadCount)
 {
 	// MS
@@ -429,7 +412,6 @@ Error Renderer::buildCommandBuffersInternal(RenderingContext& ctx, U32 threadId,
 		init.m_framebuffer = m_fs->getFramebuffer();
 		CommandBufferPtr cmdb = getGrManager().newInstance<CommandBuffer>(init);
 		cmdb->setViewport(0, 0, m_fs->getWidth(), m_fs->getHeight());
-		cmdb->setPolygonOffset(0.0, 0.0);
 
 		m_lf->run(ctx, cmdb);
 		m_fs->drawVolumetric(ctx, cmdb);
@@ -503,7 +485,12 @@ Error Renderer::buildCommandBuffers(RenderingContext& ctx)
 	return err;
 }
 
-Error Renderer::createShader(CString fname, ShaderResourcePtr& shader, CString fmt, ...)
+Error Renderer::createShader(CString fname, ShaderResourcePtr& shader, CString extra)
+{
+	return m_resources->loadResourceToCache(shader, fname, &extra[0], "r_");
+}
+
+Error Renderer::createShaderf(CString fname, ShaderResourcePtr& shader, CString fmt, ...)
 {
 	Array<char, 512> buffer;
 	va_list args;
@@ -517,4 +504,9 @@ Error Renderer::createShader(CString fname, ShaderResourcePtr& shader, CString f
 	return m_resources->loadResourceToCache(shader, fname, &buffer[0], "r_");
 }
 
+void Renderer::createDrawQuadShaderProgram(ShaderPtr frag, ShaderProgramPtr& prog)
+{
+	prog = m_gr->newInstance<ShaderProgram>(m_drawQuadVert->getGrShader(), frag);
+}
+
 } // end namespace anki

+ 15 - 5
src/anki/renderer/Renderer.h

@@ -66,7 +66,16 @@ public:
 	class Is
 	{
 	public:
-		TransientMemoryInfo m_dynBufferInfo;
+		TransientMemoryToken m_commonToken;
+		TransientMemoryToken m_pointLightsToken;
+		TransientMemoryToken m_spotLightsToken;
+		TransientMemoryToken m_probesToken;
+		TransientMemoryToken m_decalsToken;
+		TransientMemoryToken m_clustersToken;
+		TransientMemoryToken m_lightIndicesToken;
+
+		TexturePtr m_diffDecalTex;
+		TexturePtr m_normRoughnessDecalTex;
 	} m_is;
 	/// @}
 
@@ -285,7 +294,7 @@ anki_internal:
 
 	void drawQuadInstanced(CommandBufferPtr& cmdb, U32 primitiveCount)
 	{
-		cmdb->drawArrays(3, primitiveCount);
+		cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 3, primitiveCount);
 	}
 
 	/// Get the LOD given the distance of an object from the camera
@@ -294,8 +303,8 @@ anki_internal:
 		return distance / m_lodDistance;
 	}
 
-	/// Create a pipeline object that has as a vertex shader the m_drawQuadVert and the given fragment progam
-	void createDrawQuadPipeline(ShaderPtr frag, const ColorStateInfo& colorState, PipelinePtr& ppline);
+	/// Create a shader program that has as a vertex shader the m_drawQuadVert and the given fragment progam
+	void createDrawQuadShaderProgram(ShaderPtr frag, ShaderProgramPtr& prog);
 
 	/// Create a framebuffer attachment texture
 	void createRenderTarget(U32 w,
@@ -308,7 +317,8 @@ anki_internal:
 
 	void clearRenderTarget(TexturePtr rt, const ClearValue& clear, TextureUsageBit transferTo);
 
-	ANKI_USE_RESULT Error createShader(CString fname, ShaderResourcePtr& shader, CString fmt, ...);
+	ANKI_USE_RESULT Error createShader(CString fname, ShaderResourcePtr& shader, CString extra);
+	ANKI_USE_RESULT Error createShaderf(CString fname, ShaderResourcePtr& shader, CString fmt, ...);
 
 	GrManager& getGrManager()
 	{

+ 17 - 14
src/anki/renderer/Sm.cpp

@@ -23,14 +23,22 @@ Sm::~Sm()
 {
 	m_spots.destroy(getAllocator());
 	m_omnis.destroy(getAllocator());
+}
+
+Error Sm::init(const ConfigSet& config)
+{
+	ANKI_LOGI("Initializing shadowmapping");
 
-	if(m_pplineCache)
+	Error err = initInternal(config);
+	if(err)
 	{
-		getAllocator().deleteInstance(m_pplineCache);
+		ANKI_LOGE("Failed to initialize shadowmapping");
 	}
+
+	return err;
 }
 
-Error Sm::init(const ConfigSet& config)
+Error Sm::initInternal(const ConfigSet& config)
 {
 	m_poissonEnabled = config.getNumber("sm.poissonEnabled");
 	m_bilinearEnabled = config.getNumber("sm.bilinearEnabled");
@@ -98,11 +106,6 @@ Error Sm::init(const ConfigSet& config)
 		++layer;
 	}
 
-	// Init state
-	m_state.m_depthStencil.m_format = Sm::DEPTH_RT_PIXEL_FORMAT;
-
-	m_pplineCache = getAllocator().newInstance<GrObjectCache>(&getGrManager());
-
 	return ErrorCode::NONE;
 }
 
@@ -265,17 +268,17 @@ Error Sm::doSpotLight(SceneNode& light, CommandBufferPtr& cmdb, FramebufferPtr&
 	}
 
 	CommandBufferInitInfo cinf;
-	cinf.m_flags = CommandBufferFlag::SECOND_LEVEL;
+	cinf.m_flags = CommandBufferFlag::SECOND_LEVEL | CommandBufferFlag::GRAPHICS_WORK;
 	cinf.m_framebuffer = fb;
 	cmdb = m_r->getGrManager().newInstance<CommandBuffer>(cinf);
+
+	// Set state
 	cmdb->setViewport(0, 0, m_resolution, m_resolution);
 	cmdb->setPolygonOffset(1.0, 2.0);
 
 	Error err = m_r->getSceneDrawer().drawRange(Pass::SM,
 		frc,
 		cmdb,
-		*m_pplineCache,
-		m_state,
 		vis.getBegin(VisibilityGroupType::RENDERABLES_MS) + start,
 		vis.getBegin(VisibilityGroupType::RENDERABLES_MS) + end);
 
@@ -298,17 +301,17 @@ Error Sm::doOmniLight(
 		if(start != end)
 		{
 			CommandBufferInitInfo cinf;
-			cinf.m_flags = CommandBufferFlag::SECOND_LEVEL;
+			cinf.m_flags = CommandBufferFlag::SECOND_LEVEL | CommandBufferFlag::GRAPHICS_WORK;
 			cinf.m_framebuffer = fbs[frCount];
 			cmdbs[frCount] = m_r->getGrManager().newInstance<CommandBuffer>(cinf);
+
+			// Set state
 			cmdbs[frCount]->setViewport(0, 0, m_resolution, m_resolution);
 			cmdbs[frCount]->setPolygonOffset(1.0, 2.0);
 
 			ANKI_CHECK(m_r->getSceneDrawer().drawRange(Pass::SM,
 				frc,
 				cmdbs[frCount],
-				*m_pplineCache,
-				m_state,
 				vis.getBegin(VisibilityGroupType::RENDERABLES_MS) + start,
 				vis.getBegin(VisibilityGroupType::RENDERABLES_MS) + end));
 

+ 7 - 23
src/anki/renderer/Sm.h

@@ -25,6 +25,12 @@ class Sm : public RenderingPass
 anki_internal:
 	static const PixelFormat DEPTH_RT_PIXEL_FORMAT;
 
+	/// Enable Poisson for all the levels
+	Bool8 m_poissonEnabled = false;
+
+	TexturePtr m_spotTexArray;
+	TexturePtr m_omniTexArray;
+
 	Sm(Renderer* r)
 		: RenderingPass(r)
 	{
@@ -44,25 +50,7 @@ anki_internal:
 
 	void setPostRunBarriers(RenderingContext& ctx);
 
-	Bool getPoissonEnabled() const
-	{
-		return m_poissonEnabled;
-	}
-
-	TexturePtr getSpotTextureArray() const
-	{
-		return m_spotTexArray;
-	}
-
-	TexturePtr getOmniTextureArray() const
-	{
-		return m_omniTexArray;
-	}
-
 private:
-	TexturePtr m_spotTexArray;
-	TexturePtr m_omniTexArray;
-
 	class ShadowmapBase
 	{
 	public:
@@ -86,17 +74,13 @@ private:
 	DynamicArray<ShadowmapSpot> m_spots;
 	DynamicArray<ShadowmapOmni> m_omnis;
 
-	/// Enable Poisson for all the levels
-	Bool8 m_poissonEnabled = false;
-
 	/// Shadowmap bilinear filtering for the first level. Better quality
 	Bool8 m_bilinearEnabled;
 
 	/// Shadowmap resolution
 	U32 m_resolution;
 
-	GrObjectCache* m_pplineCache = nullptr;
-	PipelineInitInfo m_state;
+	ANKI_USE_RESULT Error initInternal(const ConfigSet& initializer);
 
 	/// Find the best shadowmap for that light
 	template<typename TShadowmap, typename TContainer>

+ 43 - 59
src/anki/renderer/Smaa.cpp

@@ -34,26 +34,11 @@ Error SmaaEdge::init(const ConfigSet& initializer)
 		F32(m_r->getHeight()),
 		&m_r->getSmaa().m_qualityPerset[0]);
 
-	ANKI_CHECK(getResourceManager().loadResourceToCache(m_vert, "shaders/SmaaEdge.vert.glsl", pps.toCString(), "r_"));
-	ANKI_CHECK(getResourceManager().loadResourceToCache(m_frag, "shaders/SmaaEdge.frag.glsl", pps.toCString(), "r_"));
+	ANKI_CHECK(m_r->createShader("shaders/SmaaEdge.vert.glsl", m_vert, pps.toCString()));
+	ANKI_CHECK(m_r->createShader("shaders/SmaaEdge.frag.glsl", m_frag, pps.toCString()));
 
-	// Create ppline
-	PipelineInitInfo ppinit;
-
-	ppinit.m_inputAssembler.m_topology = PrimitiveTopology::TRIANGLE_STRIP;
-
-	ppinit.m_depthStencil.m_depthWriteEnabled = false;
-	ppinit.m_depthStencil.m_depthCompareFunction = CompareOperation::ALWAYS;
-	ppinit.m_depthStencil.m_stencilFront.m_stencilPassDepthPassOperation = StencilOperation::REPLACE;
-	ppinit.m_depthStencil.m_format = STENCIL_PIXEL_FORMAT;
-
-	ppinit.m_color.m_attachmentCount = 1;
-	ppinit.m_color.m_attachments[0].m_format = EDGE_PIXEL_FORMAT;
-
-	ppinit.m_shaders[ShaderType::VERTEX] = m_vert->getGrShader();
-	ppinit.m_shaders[ShaderType::FRAGMENT] = m_frag->getGrShader();
-
-	m_ppline = gr.newInstance<Pipeline>(ppinit);
+	// Create prog
+	m_prog = getGrManager().newInstance<ShaderProgram>(m_vert->getGrShader(), m_frag->getGrShader());
 
 	// Create RT
 	m_r->createRenderTarget(m_r->getWidth(),
@@ -75,11 +60,6 @@ Error SmaaEdge::init(const ConfigSet& initializer)
 	fbInit.m_depthStencilAttachment.m_usageInsideRenderPass = TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE;
 	m_fb = gr.newInstance<Framebuffer>(fbInit);
 
-	// Create RC group
-	ResourceGroupInitInfo rcinit;
-	rcinit.m_textures[0].m_texture = m_r->getIs().getRt();
-	m_rcgroup = gr.newInstance<ResourceGroup>(rcinit);
-
 	return ErrorCode::NONE;
 }
 
@@ -112,15 +92,22 @@ void SmaaEdge::run(RenderingContext& ctx)
 	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
 
 	cmdb->setViewport(0, 0, m_r->getWidth(), m_r->getHeight());
-	cmdb->bindResourceGroup(m_rcgroup, 0, nullptr);
-	cmdb->bindPipeline(m_ppline);
+	cmdb->bindShaderProgram(m_prog);
+	cmdb->bindTexture(0, 0, m_r->getIs().getRt());
+
+	cmdb->setStencilOperations(
+		FaceSelectionMask::FRONT, StencilOperation::KEEP, StencilOperation::KEEP, StencilOperation::REPLACE);
 	cmdb->setStencilCompareMask(FaceSelectionMask::FRONT, 0xF);
 	cmdb->setStencilWriteMask(FaceSelectionMask::FRONT, 0xF);
 	cmdb->setStencilReference(FaceSelectionMask::FRONT, 0xF);
 
 	cmdb->beginRenderPass(m_fb);
-	cmdb->drawArrays(3);
+	m_r->drawQuad(cmdb);
 	cmdb->endRenderPass();
+
+	// Restore state
+	cmdb->setStencilOperations(
+		FaceSelectionMask::FRONT, StencilOperation::KEEP, StencilOperation::KEEP, StencilOperation::KEEP);
 }
 
 SmaaWeights::~SmaaWeights()
@@ -141,28 +128,11 @@ Error SmaaWeights::init(const ConfigSet& initializer)
 		F32(m_r->getHeight()),
 		&m_r->getSmaa().m_qualityPerset[0]);
 
-	ANKI_CHECK(
-		getResourceManager().loadResourceToCache(m_vert, "shaders/SmaaWeights.vert.glsl", pps.toCString(), "r_"));
-	ANKI_CHECK(
-		getResourceManager().loadResourceToCache(m_frag, "shaders/SmaaWeights.frag.glsl", pps.toCString(), "r_"));
-
-	// Create ppline
-	PipelineInitInfo ppinit;
-
-	ppinit.m_inputAssembler.m_topology = PrimitiveTopology::TRIANGLE_STRIP;
-
-	ppinit.m_depthStencil.m_depthWriteEnabled = false;
-	ppinit.m_depthStencil.m_depthCompareFunction = CompareOperation::ALWAYS;
-	ppinit.m_depthStencil.m_stencilFront.m_compareFunction = CompareOperation::EQUAL;
-	ppinit.m_depthStencil.m_format = STENCIL_PIXEL_FORMAT;
-
-	ppinit.m_color.m_attachmentCount = 1;
-	ppinit.m_color.m_attachments[0].m_format = WEIGHTS_PIXEL_FORMAT;
-
-	ppinit.m_shaders[ShaderType::VERTEX] = m_vert->getGrShader();
-	ppinit.m_shaders[ShaderType::FRAGMENT] = m_frag->getGrShader();
+	ANKI_CHECK(m_r->createShader("shaders/SmaaWeights.vert.glsl", m_vert, pps.toCString()));
+	ANKI_CHECK(m_r->createShader("shaders/SmaaWeights.frag.glsl", m_frag, pps.toCString()));
 
-	m_ppline = gr.newInstance<Pipeline>(ppinit);
+	// Create prog
+	m_prog = getGrManager().newInstance<ShaderProgram>(m_vert->getGrShader(), m_frag->getGrShader());
 
 	// Create RT
 	m_r->createRenderTarget(m_r->getWidth(),
@@ -224,13 +194,6 @@ Error SmaaWeights::init(const ConfigSet& initializer)
 	}
 	cmdb->flush();
 
-	// Create RC group
-	ResourceGroupInitInfo rcinit;
-	rcinit.m_textures[0].m_texture = m_r->getSmaa().m_edge.m_rt;
-	rcinit.m_textures[1].m_texture = m_areaTex;
-	rcinit.m_textures[2].m_texture = m_searchTex;
-	m_rcgroup = gr.newInstance<ResourceGroup>(rcinit);
-
 	return ErrorCode::NONE;
 }
 
@@ -253,19 +216,41 @@ void SmaaWeights::run(RenderingContext& ctx)
 	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
 
 	cmdb->setViewport(0, 0, m_r->getWidth(), m_r->getHeight());
-	cmdb->bindResourceGroup(m_rcgroup, 0, nullptr);
-	cmdb->bindPipeline(m_ppline);
+	cmdb->bindTexture(0, 0, m_r->getSmaa().m_edge.m_rt);
+	cmdb->bindTexture(0, 1, m_areaTex);
+	cmdb->bindTexture(0, 2, m_searchTex);
+	cmdb->bindShaderProgram(m_prog);
+
+	cmdb->setStencilCompareFunction(FaceSelectionMask::FRONT, CompareOperation::EQUAL);
 	cmdb->setStencilCompareMask(FaceSelectionMask::FRONT, 0xF);
 	cmdb->setStencilWriteMask(FaceSelectionMask::FRONT, 0x0);
 	cmdb->setStencilReference(FaceSelectionMask::FRONT, 0xF);
 
 	cmdb->beginRenderPass(m_fb);
-	cmdb->drawArrays(3);
+	m_r->drawQuad(cmdb);
 	cmdb->endRenderPass();
+
+	// Restore state
+	cmdb->setStencilCompareFunction(FaceSelectionMask::FRONT, CompareOperation::ALWAYS);
 }
 
 Error Smaa::init(const ConfigSet& cfg)
 {
+	Error err = initInternal(cfg);
+	if(err)
+	{
+		ANKI_LOGE("Failed to initialize SMAA");
+	}
+
+	return err;
+}
+
+Error Smaa::initInternal(const ConfigSet& cfg)
+{
+	m_qualityPerset = "ULTRA";
+
+	ANKI_LOGI("Initializing SMAA in %s perset", &m_qualityPerset[0]);
+
 	TextureInitInfo texinit;
 	texinit.m_format = STENCIL_PIXEL_FORMAT;
 	texinit.m_width = m_r->getWidth();
@@ -273,7 +258,6 @@ Error Smaa::init(const ConfigSet& cfg)
 	texinit.m_usage = TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE;
 	m_stencilTex = getGrManager().newInstance<Texture>(texinit);
 
-	m_qualityPerset = "ULTRA";
 	ANKI_CHECK(m_edge.init(cfg));
 	ANKI_CHECK(m_weights.init(cfg));
 	return ErrorCode::NONE;

+ 5 - 4
src/anki/renderer/Smaa.h

@@ -35,8 +35,7 @@ private:
 	FramebufferPtr m_fb;
 	ShaderResourcePtr m_vert;
 	ShaderResourcePtr m_frag;
-	PipelinePtr m_ppline;
-	ResourceGroupPtr m_rcgroup;
+	ShaderProgramPtr m_prog;
 };
 
 class SmaaWeights : public RenderingPass
@@ -61,10 +60,9 @@ private:
 	FramebufferPtr m_fb;
 	ShaderResourcePtr m_vert;
 	ShaderResourcePtr m_frag;
-	PipelinePtr m_ppline;
+	ShaderProgramPtr m_prog;
 	TexturePtr m_areaTex;
 	TexturePtr m_searchTex;
-	ResourceGroupPtr m_rcgroup;
 };
 
 class Smaa : public RenderingPass
@@ -87,6 +85,9 @@ anki_internal:
 	}
 
 	ANKI_USE_RESULT Error init(const ConfigSet& cfg);
+
+private:
+	ANKI_USE_RESULT Error initInternal(const ConfigSet& cfg);
 };
 /// @}
 

+ 40 - 90
src/anki/renderer/Ssao.cpp

@@ -119,16 +119,14 @@ Error Ssao::initInternal(const ConfigSet& config)
 	genNoise(&noise[0], &noise[0] + noise.getSize());
 
 	CommandBufferInitInfo cmdbInit;
-	cmdbInit.m_flags = CommandBufferFlag::SMALL_BATCH;
+	cmdbInit.m_flags = CommandBufferFlag::SMALL_BATCH | CommandBufferFlag::TRANSFER_WORK;
 
 	CommandBufferPtr cmdb = gr.newInstance<CommandBuffer>(cmdbInit);
 
 	TextureSurfaceInfo surf(0, 0, 0, 0);
 
 	cmdb->setTextureSurfaceBarrier(m_noiseTex, TextureUsageBit::NONE, TextureUsageBit::UPLOAD, surf);
-
 	cmdb->uploadTextureSurfaceCopyData(m_noiseTex, surf, &noise[0], sizeof(noise));
-
 	cmdb->setTextureSurfaceBarrier(m_noiseTex, TextureUsageBit::UPLOAD, TextureUsageBit::SAMPLED_FRAGMENT, surf);
 
 	cmdb->flush();
@@ -154,103 +152,51 @@ Error Ssao::initInternal(const ConfigSet& config)
 	//
 	// Shaders
 	//
-	PipelineInitInfo ppinit;
-	ppinit.m_color.m_attachmentCount = 1;
-	ppinit.m_color.m_attachments[0].m_format = RT_PIXEL_FORMAT;
-	ppinit.m_depthStencil.m_depthWriteEnabled = false;
-	ppinit.m_depthStencil.m_depthCompareFunction = CompareOperation::ALWAYS;
-
-	StringAuto pps(getAllocator());
-
-	// vert shader
-	ANKI_CHECK(getResourceManager().loadResource("shaders/Quad.vert.glsl", m_quadVert));
-
-	ppinit.m_shaders[ShaderType::VERTEX] = m_quadVert->getGrShader();
 
 	// main pass prog
-	pps.destroy();
-	pps.sprintf("#define NOISE_MAP_SIZE %u\n"
-				"#define WIDTH %u\n"
-				"#define HEIGHT %u\n"
-				"#define KERNEL_SIZE %u\n"
-				"#define KERNEL_ARRAY %s\n"
-				"#define RADIUS float(%f)\n",
+	ANKI_CHECK(m_r->createShaderf("shaders/Ssao.frag.glsl",
+		m_ssaoFrag,
+		"#define NOISE_MAP_SIZE %u\n"
+		"#define WIDTH %u\n"
+		"#define HEIGHT %u\n"
+		"#define KERNEL_SIZE %u\n"
+		"#define KERNEL_ARRAY %s\n"
+		"#define RADIUS float(%f)\n",
 		NOISE_TEX_SIZE,
 		m_width,
 		m_height,
 		KERNEL_SIZE,
 		&kernelStr[0],
-		HEMISPHERE_RADIUS);
-
-	ANKI_CHECK(getResourceManager().loadResourceToCache(m_ssaoFrag, "shaders/Ssao.frag.glsl", pps.toCString(), "r_"));
+		HEMISPHERE_RADIUS));
 
-	ppinit.m_shaders[ShaderType::FRAGMENT] = m_ssaoFrag->getGrShader();
-
-	m_ssaoPpline = getGrManager().newInstance<Pipeline>(ppinit);
+	m_r->createDrawQuadShaderProgram(m_ssaoFrag->getGrShader(), m_ssaoProg);
 
 	// h blur
 	const char* SHADER_FILENAME = "shaders/GaussianBlurGeneric.frag.glsl";
 
-	pps.destroy();
-	pps.sprintf("#define HPASS\n"
-				"#define COL_R\n"
-				"#define TEXTURE_SIZE vec2(%f, %f)\n"
-				"#define KERNEL_SIZE 13\n",
+	ANKI_CHECK(m_r->createShaderf(SHADER_FILENAME,
+		m_hblurFrag,
+		"#define HPASS\n"
+		"#define COL_R\n"
+		"#define TEXTURE_SIZE vec2(%f, %f)\n"
+		"#define KERNEL_SIZE 13\n",
 		F32(m_width),
-		F32(m_height));
-
-	ANKI_CHECK(getResourceManager().loadResourceToCache(m_hblurFrag, SHADER_FILENAME, pps.toCString(), "r_"));
-
-	ppinit.m_shaders[ShaderType::FRAGMENT] = m_hblurFrag->getGrShader();
+		F32(m_height)));
 
-	m_hblurPpline = getGrManager().newInstance<Pipeline>(ppinit);
+	m_r->createDrawQuadShaderProgram(m_hblurFrag->getGrShader(), m_hblurProg);
 
 	// v blur
-	pps.destroy();
-	pps.sprintf("#define VPASS\n"
-				"#define COL_R\n"
-				"#define TEXTURE_SIZE vec2(%f, %f)\n"
-				"#define KERNEL_SIZE 11\n",
+	ANKI_CHECK(m_r->createShaderf(SHADER_FILENAME,
+		m_vblurFrag,
+		"#define VPASS\n"
+		"#define COL_R\n"
+		"#define TEXTURE_SIZE vec2(%f, %f)\n"
+		"#define KERNEL_SIZE 11\n",
 		F32(m_width),
-		F32(m_height));
-
-	ANKI_CHECK(getResourceManager().loadResourceToCache(m_vblurFrag, SHADER_FILENAME, pps.toCString(), "r_"));
-
-	ppinit.m_shaders[ShaderType::FRAGMENT] = m_vblurFrag->getGrShader();
+		F32(m_height)));
 
-	m_vblurPpline = getGrManager().newInstance<Pipeline>(ppinit);
+	m_r->createDrawQuadShaderProgram(m_vblurFrag->getGrShader(), m_vblurProg);
 
-	//
-	// Resource groups
-	//
-	ResourceGroupInitInfo rcinit;
-	SamplerInitInfo sinit;
-	sinit.m_minMagFilter = SamplingFilter::LINEAR;
-	sinit.m_mipmapFilter = SamplingFilter::NEAREST;
-	sinit.m_repeat = false;
-
-	rcinit.m_textures[0].m_texture = m_r->getDepthDownscale().m_qd.m_depthRt;
-	rcinit.m_textures[0].m_sampler = gr.newInstance<Sampler>(sinit);
-	rcinit.m_textures[0].m_usage = TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ;
-
-	rcinit.m_textures[1].m_texture = m_r->getMs().m_rt2;
-	rcinit.m_textures[1].m_sampler = rcinit.m_textures[0].m_sampler;
-
-	rcinit.m_textures[2].m_texture = m_noiseTex;
-
-	rcinit.m_uniformBuffers[0].m_uploadedMemory = true;
-	rcinit.m_uniformBuffers[0].m_usage = BufferUsageBit::UNIFORM_FRAGMENT;
-	m_rcFirst = gr.newInstance<ResourceGroup>(rcinit);
-
-	rcinit = ResourceGroupInitInfo();
-	rcinit.m_textures[0].m_texture = m_vblurRt;
-	m_hblurRc = gr.newInstance<ResourceGroup>(rcinit);
-
-	rcinit = ResourceGroupInitInfo();
-	rcinit.m_textures[0].m_texture = m_hblurRt;
-	m_vblurRc = gr.newInstance<ResourceGroup>(rcinit);
-
-	gr.finish();
 	return ErrorCode::NONE;
 }
 
@@ -290,11 +236,15 @@ void Ssao::run(RenderingContext& ctx)
 	//
 	cmdb->beginRenderPass(m_vblurFb);
 	cmdb->setViewport(0, 0, m_width, m_height);
-	cmdb->bindPipeline(m_ssaoPpline);
+	cmdb->bindShaderProgram(m_ssaoProg);
+
+	cmdb->bindTexture(0, 0, m_r->getDepthDownscale().m_qd.m_depthRt);
+	cmdb->bindTexture(0, 1, m_r->getMs().m_rt2);
+	cmdb->bindTexture(0, 2, m_noiseTex);
 
-	TransientMemoryInfo inf;
-	Vec4* unis = static_cast<Vec4*>(getGrManager().allocateFrameTransientMemory(
-		sizeof(Vec4) * 2, BufferUsageBit::UNIFORM_ALL, inf.m_uniformBuffers[0]));
+	TransientMemoryToken token;
+	Vec4* unis = static_cast<Vec4*>(
+		getGrManager().allocateFrameTransientMemory(sizeof(Vec4) * 2, BufferUsageBit::UNIFORM_ALL, token));
 
 	const FrustumComponent& frc = *ctx.m_frustumComponent;
 	const Mat4& pmat = frc.getProjectionMatrix();
@@ -302,7 +252,7 @@ void Ssao::run(RenderingContext& ctx)
 	++unis;
 	*unis = Vec4(pmat(0, 0), pmat(1, 1), pmat(2, 2), pmat(2, 3));
 
-	cmdb->bindResourceGroup(m_rcFirst, 0, &inf);
+	cmdb->bindUniformBuffer(0, 0, token);
 
 	// Draw
 	m_r->drawQuad(cmdb);
@@ -322,8 +272,8 @@ void Ssao::run(RenderingContext& ctx)
 			TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 			TextureSurfaceInfo(0, 0, 0, 0));
 		cmdb->beginRenderPass(m_hblurFb);
-		cmdb->bindPipeline(m_hblurPpline);
-		cmdb->bindResourceGroup(m_hblurRc, 0, nullptr);
+		cmdb->bindShaderProgram(m_hblurProg);
+		cmdb->bindTexture(0, 0, m_vblurRt);
 		m_r->drawQuad(cmdb);
 		cmdb->endRenderPass();
 
@@ -337,8 +287,8 @@ void Ssao::run(RenderingContext& ctx)
 			TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 			TextureSurfaceInfo(0, 0, 0, 0));
 		cmdb->beginRenderPass(m_vblurFb);
-		cmdb->bindPipeline(m_vblurPpline);
-		cmdb->bindResourceGroup(m_vblurRc, 0, nullptr);
+		cmdb->bindShaderProgram(m_vblurProg);
+		cmdb->bindTexture(0, 0, m_hblurRt);
 		m_r->drawQuad(cmdb);
 		cmdb->endRenderPass();
 	}

+ 3 - 8
src/anki/renderer/Ssao.h

@@ -48,20 +48,15 @@ private:
 	FramebufferPtr m_vblurFb;
 	FramebufferPtr m_hblurFb;
 
-	ShaderResourcePtr m_quadVert;
 	ShaderResourcePtr m_ssaoFrag;
 	ShaderResourcePtr m_hblurFrag;
 	ShaderResourcePtr m_vblurFrag;
-	PipelinePtr m_ssaoPpline;
-	PipelinePtr m_hblurPpline;
-	PipelinePtr m_vblurPpline;
+	ShaderProgramPtr m_ssaoProg;
+	ShaderProgramPtr m_hblurProg;
+	ShaderProgramPtr m_vblurProg;
 
 	TexturePtr m_noiseTex;
 
-	ResourceGroupPtr m_rcFirst;
-	ResourceGroupPtr m_hblurRc;
-	ResourceGroupPtr m_vblurRc;
-
 	ANKI_USE_RESULT Error createFb(FramebufferPtr& fb, TexturePtr& rt);
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& initializer);
 };

+ 15 - 26
src/anki/renderer/Sslf.cpp

@@ -13,10 +13,12 @@ namespace anki
 
 Error Sslf::init(const ConfigSet& config)
 {
+	ANKI_LOGI("Initializing screen space lens flare");
+
 	Error err = initInternal(config);
 	if(err)
 	{
-		ANKI_LOGE("Failed to init screen space lens flare pass");
+		ANKI_LOGE("Failed to init screen space lens flare");
 	}
 
 	return err;
@@ -24,34 +26,16 @@ Error Sslf::init(const ConfigSet& config)
 
 Error Sslf::initInternal(const ConfigSet& config)
 {
-	// Load program 1
-	StringAuto pps(getAllocator());
-
-	pps.sprintf("#define TEX_DIMENSIONS vec2(%u.0, %u.0)\n",
+	ANKI_CHECK(m_r->createShaderf("shaders/Sslf.frag.glsl",
+		m_frag,
+		"#define TEX_DIMENSIONS vec2(%u.0, %u.0)\n",
 		m_r->getBloom().m_extractExposure.m_width,
-		m_r->getBloom().m_extractExposure.m_height);
-
-	ANKI_CHECK(getResourceManager().loadResourceToCache(m_frag, "shaders/Sslf.frag.glsl", pps.toCString(), "r_"));
-
-	ColorStateInfo colorState;
-	colorState.m_attachmentCount = 1;
-	colorState.m_attachments[0].m_format = BLOOM_RT_PIXEL_FORMAT;
-	colorState.m_attachments[0].m_srcBlendMethod = BlendMethod::ONE;
-	colorState.m_attachments[0].m_dstBlendMethod = BlendMethod::ONE;
+		m_r->getBloom().m_extractExposure.m_height));
 
-	m_r->createDrawQuadPipeline(m_frag->getGrShader(), colorState, m_ppline);
+	m_r->createDrawQuadShaderProgram(m_frag->getGrShader(), m_prog);
 
-	// Textures
 	ANKI_CHECK(getResourceManager().loadResource("engine_data/LensDirt.ankitex", m_lensDirtTex));
 
-	// Create the resource group
-	ResourceGroupInitInfo rcInit;
-	rcInit.m_textures[0].m_texture = m_r->getBloom().m_extractExposure.m_rt;
-	rcInit.m_textures[1].m_texture = m_lensDirtTex->getGrTexture();
-
-	m_rcGroup = getGrManager().newInstance<ResourceGroup>(rcInit);
-
-	getGrManager().finish();
 	return ErrorCode::NONE;
 }
 
@@ -60,10 +44,15 @@ void Sslf::run(RenderingContext& ctx)
 	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
 
 	// Draw to the SSLF FB
-	cmdb->bindPipeline(m_ppline);
-	cmdb->bindResourceGroup(m_rcGroup, 0, nullptr);
+	cmdb->bindShaderProgram(m_prog);
+	cmdb->setBlendMethods(0, BlendMethod::ONE, BlendMethod::ONE);
+	cmdb->bindTexture(0, 0, m_r->getBloom().m_extractExposure.m_rt);
+	cmdb->bindTexture(0, 1, m_lensDirtTex->getGrTexture());
 
 	m_r->drawQuad(cmdb);
+
+	// Retore state
+	cmdb->setBlendMethods(0, BlendMethod::ONE, BlendMethod::ZERO);
 }
 
 } // end namespace anki

+ 1 - 2
src/anki/renderer/Sslf.h

@@ -27,9 +27,8 @@ anki_internal:
 
 private:
 	ShaderResourcePtr m_frag;
-	PipelinePtr m_ppline;
+	ShaderProgramPtr m_prog;
 	TextureResourcePtr m_lensDirtTex;
-	ResourceGroupPtr m_rcGroup;
 
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& config);
 };

+ 0 - 205
src/anki/renderer/Tiler.cpp

@@ -1,205 +0,0 @@
-// 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/renderer/Tiler.h>
-#include <anki/renderer/Renderer.h>
-#include <anki/renderer/Ms.h>
-#include <anki/resource/ShaderResource.h>
-#include <anki/scene/FrustumComponent.h>
-#include <anki/scene/MoveComponent.h>
-#include <anki/scene/SceneNode.h>
-
-namespace anki
-{
-
-Tiler::Tiler(Renderer* r)
-	: RenderingPass(r)
-{
-}
-
-Tiler::~Tiler()
-{
-	m_currentMinMax.destroy(getAllocator());
-}
-
-Error Tiler::init()
-{
-	Error err = initInternal();
-
-	if(err)
-	{
-		ANKI_LOGE("Failed to init tiler");
-	}
-
-	return err;
-}
-
-Error Tiler::initInternal()
-{
-	// Load the program
-	StringAuto pps(getAllocator());
-
-	pps.sprintf("#define TILE_SIZE_X %u\n"
-				"#define TILE_SIZE_Y %u\n"
-				"#define TILES_COUNT_X %u\n"
-				"#define TILES_COUNT_Y %u\n",
-		TILE_SIZE,
-		TILE_SIZE,
-		m_r->getTileCountXY().x(),
-		m_r->getTileCountXY().y());
-
-	ANKI_CHECK(
-		getResourceManager().loadResourceToCache(m_shader, "shaders/TilerMinMax.comp.glsl", pps.toCString(), "r_"));
-
-	PipelineInitInfo pplineInit;
-	pplineInit.m_shaders[U(ShaderType::COMPUTE)] = m_shader->getGrShader();
-	m_ppline = getGrManager().newInstance<Pipeline>(pplineInit);
-
-	// Allocate the buffers
-	U pboSize = m_r->getTileCount() * sizeof(Vec2); // The pixel size
-
-	for(U i = 0; i < m_outBuffers.getSize(); ++i)
-	{
-		// Create the buffer
-		m_outBuffers[i] =
-			getGrManager().newInstance<Buffer>(pboSize, BufferUsageBit::STORAGE_ALL, BufferMapAccessBit::READ);
-
-		// Create graphics resources
-		ResourceGroupInitInfo rcinit;
-		rcinit.m_storageBuffers[0].m_buffer = m_outBuffers[i];
-		rcinit.m_textures[0].m_texture = m_r->getMs().m_depthRt;
-
-		m_rcGroups[i] = getGrManager().newInstance<ResourceGroup>(rcinit);
-	}
-
-	m_currentMinMax.create(getAllocator(), m_r->getTileCount());
-
-	return ErrorCode::NONE;
-}
-
-void Tiler::run(CommandBufferPtr& cmd)
-{
-	// Issue the min/max job
-	U pboIdx = m_r->getFrameCount() % m_outBuffers.getSize();
-
-	cmd->bindPipeline(m_ppline);
-	cmd->bindResourceGroup(m_rcGroups[pboIdx], 0, nullptr);
-
-	cmd->dispatchCompute(m_r->getTileCountXY().x(), m_r->getTileCountXY().y(), 1);
-}
-
-void Tiler::prepareForVisibilityTests(const SceneNode& node)
-{
-	// Get the min max
-	U size = m_r->getTileCount() * sizeof(Vec2);
-
-	U buffIdx = max<U>(m_r->getFrameCount() % m_outBuffers.getSize(), 2u) - 2;
-	BufferPtr& buff = m_outBuffers[buffIdx];
-	void* mappedMem = buff->map(0, size, BufferMapAccessBit::READ);
-
-	ANKI_ASSERT(mappedMem);
-	memcpy(&m_currentMinMax[0], mappedMem, size);
-
-	buff->unmap();
-
-	// Convert the min max to view space
-	const FrustumComponent& frc = node.getComponent<FrustumComponent>();
-	const Vec4& projParams = frc.getProjectionParameters();
-
-	for(Vec2& v : m_currentMinMax)
-	{
-		// Unproject
-		v = projParams.z() / (projParams.w() + v);
-	};
-
-	// Other
-	const MoveComponent& movec = node.getComponent<MoveComponent>();
-	m_nearPlaneWspace = Plane(Vec4(0.0, 0.0, -1.0, 0.0), frc.getFrustum().getNear());
-	m_nearPlaneWspace.transform(movec.getWorldTransform());
-
-	m_viewProjMat = frc.getViewProjectionMatrix();
-	m_near = frc.getFrustum().getNear();
-}
-
-Bool Tiler::test(const CollisionShape& cs, const Aabb& aabb) const
-{
-	// Compute the distance from the near plane
-	F32 dist = aabb.testPlane(m_nearPlaneWspace);
-	if(dist <= 0.0)
-	{
-		// Collides with the near plane. The following tests will fail
-		return true;
-	}
-
-	dist += m_near;
-	dist = -dist; // Because m_nearPlaneWspace has negatives
-
-	// Find the tiles that affect it
-	const Vec4& minv = aabb.getMin();
-	const Vec4& maxv = aabb.getMax();
-	Array<Vec4, 8> points;
-	points[0] = minv.xyz1();
-	points[1] = Vec4(minv.x(), maxv.y(), minv.z(), 1.0);
-	points[2] = Vec4(minv.x(), maxv.y(), maxv.z(), 1.0);
-	points[3] = Vec4(minv.x(), minv.y(), maxv.z(), 1.0);
-	points[4] = maxv.xyz1();
-	points[5] = Vec4(maxv.x(), minv.y(), maxv.z(), 1.0);
-	points[6] = Vec4(maxv.x(), minv.y(), minv.z(), 1.0);
-	points[7] = Vec4(maxv.x(), maxv.y(), minv.z(), 1.0);
-	Vec2 min2(MAX_F32), max2(MIN_F32);
-	for(Vec4& p : points)
-	{
-		p = m_viewProjMat * p;
-		ANKI_ASSERT(p.w() > 0.0);
-		p = p.perspectiveDivide();
-
-		for(U i = 0; i < 2; ++i)
-		{
-			min2[i] = min(min2[i], p[i]);
-			max2[i] = max(max2[i], p[i]);
-		}
-	}
-
-	min2 = min2 * 0.5 + 0.5;
-	max2 = max2 * 0.5 + 0.5;
-
-	F32 tcountX = m_r->getTileCountXY().x();
-	F32 tcountY = m_r->getTileCountXY().y();
-
-	I xBegin = floor(tcountX * min2.x());
-	xBegin = clamp<I>(xBegin, 0, tcountX);
-
-	I xEnd = ceil(tcountX * max2.x());
-	xEnd = min<U>(xEnd, tcountX);
-
-	I yBegin = floor(tcountY * min2.y());
-	yBegin = clamp<I>(yBegin, 0, tcountY);
-
-	I yEnd = ceil(tcountY * max2.y());
-	yEnd = min<I>(yEnd, tcountY);
-
-	ANKI_ASSERT(xBegin >= 0 && xBegin <= tcountX && xEnd >= 0 && xEnd <= tcountX);
-	ANKI_ASSERT(yBegin >= 0 && yBegin <= tcountX && yEnd >= 0 && yBegin <= tcountY);
-
-	// Check every tile
-	U visibleCount = (yEnd - yBegin) * (xEnd - xBegin);
-	for(I y = yBegin; y < yEnd; y++)
-	{
-		for(I x = xBegin; x < xEnd; x++)
-		{
-			U tileIdx = y * tcountX + x;
-			F32 tileMaxDist = m_currentMinMax[tileIdx].y();
-
-			if(dist < tileMaxDist)
-			{
-				--visibleCount;
-			}
-		}
-	}
-
-	return visibleCount > 0;
-}
-
-} // end namespace anki

+ 0 - 57
src/anki/renderer/Tiler.h

@@ -1,57 +0,0 @@
-// 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/Collision.h>
-#include <anki/renderer/RenderingPass.h>
-#include <anki/core/Timestamp.h>
-
-namespace anki
-{
-
-class SceneNode;
-
-/// @addtogroup renderer
-/// @{
-
-/// Tiler used for visibility tests
-class Tiler : public RenderingPass
-{
-	friend class UpdatePlanesPerspectiveCameraTask;
-
-anki_internal:
-	Tiler(Renderer* r);
-	~Tiler();
-
-	ANKI_USE_RESULT Error init();
-
-	/// Test against all tiles.
-	Bool test(const CollisionShape& cs, const Aabb& aabb) const;
-
-	/// Issue the GPU job
-	void run(CommandBufferPtr& cmd);
-
-	/// Prepare for visibility tests
-	void prepareForVisibilityTests(const SceneNode& node);
-
-private:
-	// GPU objects
-	Array<BufferPtr, MAX_FRAMES_IN_FLIGHT> m_outBuffers;
-	Array<ResourceGroupPtr, MAX_FRAMES_IN_FLIGHT> m_rcGroups;
-	ShaderResourcePtr m_shader;
-	PipelinePtr m_ppline;
-
-	// Other
-	DynamicArray<Vec2> m_currentMinMax;
-	Mat4 m_viewProjMat;
-	F32 m_near;
-	Plane m_nearPlaneWspace;
-
-	ANKI_USE_RESULT Error initInternal();
-};
-/// @}
-
-} // end namespace anki

+ 28 - 28
src/anki/renderer/Tm.cpp

@@ -10,58 +10,58 @@
 namespace anki
 {
 
-Error Tm::create(const ConfigSet& initializer)
+Error Tm::init(const ConfigSet& cfg)
 {
-	// Create shader
-	StringAuto pps(getAllocator());
+	ANKI_LOGI("Initializing tonemapping");
+	Error err = initInternal(cfg);
+	if(err)
+	{
+		ANKI_LOGE("Failed to initialize tonemapping");
+	}
+
+	return err;
+}
 
+Error Tm::initInternal(const ConfigSet& initializer)
+{
+	// Create shader
 	ANKI_ASSERT(m_r->getIs().getRtMipmapCount() > 1);
-	pps.sprintf("#define IS_RT_MIPMAP %u\n"
-				"#define ANKI_RENDERER_WIDTH %u\n"
-				"#define ANKI_RENDERER_HEIGHT %u\n",
+
+	ANKI_CHECK(m_r->createShaderf("shaders/TmAverageLuminance.comp.glsl",
+		m_luminanceShader,
+		"#define IS_RT_MIPMAP %u\n"
+		"#define ANKI_RENDERER_WIDTH %u\n"
+		"#define ANKI_RENDERER_HEIGHT %u\n",
 		m_r->getIs().getRtMipmapCount() - 1,
 		m_r->getWidth(),
-		m_r->getHeight());
-
-	ANKI_CHECK(getResourceManager().loadResourceToCache(
-		m_luminanceShader, "shaders/TmAverageLuminance.comp.glsl", pps.toCString(), "r_"));
+		m_r->getHeight()));
 
-	// Create ppline
-	PipelineInitInfo pplineInit;
-	pplineInit.m_shaders[U(ShaderType::COMPUTE)] = m_luminanceShader->getGrShader();
-	m_luminancePpline = getGrManager().newInstance<Pipeline>(pplineInit);
+	// Create prog
+	m_prog = getGrManager().newInstance<ShaderProgram>(m_luminanceShader->getGrShader());
 
 	// Create buffer
 	m_luminanceBuff = getGrManager().newInstance<Buffer>(sizeof(Vec4),
 		BufferUsageBit::STORAGE_ALL | BufferUsageBit::UNIFORM_ALL | BufferUsageBit::BUFFER_UPLOAD_DESTINATION,
 		BufferMapAccessBit::NONE);
 
-	CommandBufferPtr cmdb = getGrManager().newInstance<CommandBuffer>(CommandBufferInitInfo());
+	CommandBufferInitInfo cmdbinit;
+	cmdbinit.m_flags = CommandBufferFlag::SMALL_BATCH | CommandBufferFlag::TRANSFER_WORK;
+	CommandBufferPtr cmdb = getGrManager().newInstance<CommandBuffer>(cmdbinit);
 	TransientMemoryToken token;
 	void* data = getGrManager().allocateFrameTransientMemory(sizeof(Vec4), BufferUsageBit::BUFFER_UPLOAD_SOURCE, token);
 	*static_cast<Vec4*>(data) = Vec4(0.5);
 	cmdb->uploadBuffer(m_luminanceBuff, 0, token);
 	cmdb->flush();
 
-	// Create descriptors
-	ResourceGroupInitInfo rcinit;
-	rcinit.m_storageBuffers[0].m_buffer = m_luminanceBuff;
-	rcinit.m_storageBuffers[0].m_range = sizeof(Vec4);
-	rcinit.m_storageBuffers[0].m_usage = BufferUsageBit::STORAGE_COMPUTE_READ_WRITE;
-	rcinit.m_textures[0].m_texture = m_r->getIs().getRt();
-	rcinit.m_textures[0].m_usage = TextureUsageBit::SAMPLED_COMPUTE;
-
-	m_rcGroup = getGrManager().newInstance<ResourceGroup>(rcinit);
-
-	getGrManager().finish();
 	return ErrorCode::NONE;
 }
 
 void Tm::run(RenderingContext& ctx)
 {
 	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
-	cmdb->bindPipeline(m_luminancePpline);
-	cmdb->bindResourceGroup(m_rcGroup, 0, nullptr);
+	cmdb->bindShaderProgram(m_prog);
+	cmdb->bindStorageBuffer(0, 0, m_luminanceBuff, 0);
+	cmdb->bindTexture(0, 0, m_r->getIs().getRt());
 
 	cmdb->dispatchCompute(1, 1, 1);
 }

+ 6 - 9
src/anki/renderer/Tm.h

@@ -17,25 +17,22 @@ namespace anki
 class Tm : public RenderingPass
 {
 anki_internal:
+	BufferPtr m_luminanceBuff;
+
 	Tm(Renderer* r)
 		: RenderingPass(r)
 	{
 	}
 
-	BufferPtr& getAverageLuminanceBuffer()
-	{
-		return m_luminanceBuff;
-	}
-
-	ANKI_USE_RESULT Error create(const ConfigSet& initializer);
+	ANKI_USE_RESULT Error init(const ConfigSet& cfg);
 
 	void run(RenderingContext& ctx);
 
 private:
 	ShaderResourcePtr m_luminanceShader;
-	PipelinePtr m_luminancePpline;
-	BufferPtr m_luminanceBuff;
-	ResourceGroupPtr m_rcGroup;
+	ShaderProgramPtr m_prog;
+
+	ANKI_USE_RESULT Error initInternal(const ConfigSet& cfg);
 };
 /// @}
 

+ 39 - 35
src/anki/renderer/Volumetric.cpp

@@ -19,6 +19,19 @@ Volumetric::~Volumetric()
 }
 
 Error Volumetric::init(const ConfigSet& config)
+{
+	ANKI_LOGI("Initializing volumetric pass");
+
+	Error err = initInternal(config);
+	if(err)
+	{
+		ANKI_LOGE("Failed to initialize volumetric pass");
+	}
+
+	return err;
+}
+
+Error Volumetric::initInternal(const ConfigSet& config)
 {
 	U width = m_r->getWidth() / VOLUMETRIC_FRACTION;
 	U height = m_r->getHeight() / VOLUMETRIC_FRACTION;
@@ -35,7 +48,7 @@ Error Volumetric::init(const ConfigSet& config)
 	m_r->clearRenderTarget(m_rt, ClearValue(), TextureUsageBit::SAMPLED_FRAGMENT);
 
 	// Create shaders
-	ANKI_CHECK(m_r->createShader("shaders/Volumetric.frag.glsl",
+	ANKI_CHECK(m_r->createShaderf("shaders/Volumetric.frag.glsl",
 		m_frag,
 		"#define RPASS_SIZE uvec2(%uu, %uu)\n"
 		"#define CLUSTER_COUNT uvec3(%uu, %uu, %uu)\n",
@@ -45,34 +58,8 @@ Error Volumetric::init(const ConfigSet& config)
 		m_r->getIs().getLightBin().getClusterer().getClusterCountY(),
 		m_r->getIs().getLightBin().getClusterer().getClusterCountZ()));
 
-	// Create pplines
-	ColorStateInfo state;
-	state.m_attachmentCount = 1;
-	state.m_attachments[0].m_format = IS_COLOR_ATTACHMENT_PIXEL_FORMAT;
-	state.m_attachments[0].m_srcBlendMethod = BlendMethod::SRC_ALPHA;
-	state.m_attachments[0].m_dstBlendMethod = BlendMethod::ONE_MINUS_SRC_ALPHA;
-
-	m_r->createDrawQuadPipeline(m_frag->getGrShader(), state, m_ppline);
-
-	// Create the resource groups
-	ResourceGroupInitInfo rcInit;
-	rcInit.m_textures[0].m_texture = m_r->getDepthDownscale().m_qd.m_depthRt;
-	rcInit.m_textures[0].m_usage = TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ;
-	rcInit.m_textures[1].m_texture = m_r->getSm().getSpotTextureArray();
-	rcInit.m_textures[2].m_texture = m_r->getSm().getOmniTextureArray();
-	rcInit.m_uniformBuffers[0].m_uploadedMemory = true;
-	rcInit.m_uniformBuffers[0].m_usage = BufferUsageBit::UNIFORM_FRAGMENT;
-	rcInit.m_uniformBuffers[1].m_uploadedMemory = true;
-	rcInit.m_uniformBuffers[1].m_usage = BufferUsageBit::UNIFORM_FRAGMENT;
-	rcInit.m_uniformBuffers[2].m_uploadedMemory = true;
-	rcInit.m_uniformBuffers[2].m_usage = BufferUsageBit::UNIFORM_FRAGMENT;
-	rcInit.m_uniformBuffers[3].m_uploadedMemory = true;
-	rcInit.m_uniformBuffers[3].m_usage = BufferUsageBit::UNIFORM_FRAGMENT;
-	rcInit.m_storageBuffers[0].m_uploadedMemory = true;
-	rcInit.m_storageBuffers[0].m_usage = BufferUsageBit::STORAGE_FRAGMENT_READ;
-	rcInit.m_storageBuffers[1].m_uploadedMemory = true;
-	rcInit.m_storageBuffers[1].m_usage = BufferUsageBit::STORAGE_FRAGMENT_READ;
-	m_rc = getGrManager().newInstance<ResourceGroup>(rcInit);
+	// Create prog
+	m_r->createDrawQuadShaderProgram(m_frag->getGrShader(), m_prog);
 
 	// Create FBs
 	FramebufferInitInfo fbInit;
@@ -107,20 +94,37 @@ void Volumetric::run(RenderingContext& ctx)
 	const Frustum& frc = ctx.m_frustumComponent->getFrustum();
 
 	// Update uniforms
-	TransientMemoryInfo dyn = ctx.m_is.m_dynBufferInfo;
-	Vec4* uniforms = static_cast<Vec4*>(getGrManager().allocateFrameTransientMemory(
-		sizeof(Vec4) * 2, BufferUsageBit::UNIFORM_ALL, dyn.m_uniformBuffers[3]));
+	TransientMemoryToken token;
+	Vec4* uniforms = static_cast<Vec4*>(
+		getGrManager().allocateFrameTransientMemory(sizeof(Vec4) * 2, BufferUsageBit::UNIFORM_ALL, token));
 	computeLinearizeDepthOptimal(frc.getNear(), frc.getFar(), uniforms[0].x(), uniforms[0].y());
 
 	uniforms[1] = Vec4(m_fogColor, m_fogFactor);
 
-	// pass 0
+	// pass
 	cmdb->setViewport(0, 0, m_r->getWidth() / VOLUMETRIC_FRACTION, m_r->getHeight() / VOLUMETRIC_FRACTION);
+	cmdb->setBlendMethods(0, BlendMethod::SRC_ALPHA, BlendMethod::ONE_MINUS_SRC_ALPHA);
+
+	cmdb->bindTexture(0, 0, m_r->getDepthDownscale().m_qd.m_depthRt);
+	cmdb->bindTexture(0, 1, m_r->getSm().m_spotTexArray);
+	cmdb->bindTexture(0, 2, m_r->getSm().m_omniTexArray);
+
+	cmdb->bindUniformBuffer(0, 0, ctx.m_is.m_commonToken);
+	cmdb->bindUniformBuffer(0, 1, ctx.m_is.m_pointLightsToken);
+	cmdb->bindUniformBuffer(0, 2, ctx.m_is.m_spotLightsToken);
+	cmdb->bindUniformBuffer(0, 3, token);
+
+	cmdb->bindStorageBuffer(0, 0, ctx.m_is.m_clustersToken);
+	cmdb->bindStorageBuffer(0, 1, ctx.m_is.m_lightIndicesToken);
+
+	cmdb->bindShaderProgram(m_prog);
+
 	cmdb->beginRenderPass(m_fb);
-	cmdb->bindPipeline(m_ppline);
-	cmdb->bindResourceGroup(m_rc, 0, &dyn);
 	m_r->drawQuad(cmdb);
 	cmdb->endRenderPass();
+
+	// Restore state
+	cmdb->setBlendMethods(0, BlendMethod::ONE, BlendMethod::ZERO);
 }
 
 } // end namespace anki

+ 3 - 2
src/anki/renderer/Volumetric.h

@@ -40,13 +40,14 @@ anki_internal:
 	void setPostRunBarriers(RenderingContext& ctx);
 
 private:
-	ResourceGroupPtr m_rc;
 	ShaderResourcePtr m_frag;
-	PipelinePtr m_ppline;
+	ShaderProgramPtr m_prog;
 	FramebufferPtr m_fb;
 
 	Vec3 m_fogColor = Vec3(1.0);
 	F32 m_fogFactor = 1.0;
+
+	ANKI_USE_RESULT Error initInternal(const ConfigSet& config);
 };
 /// @}
 

+ 10 - 44
src/anki/resource/Material.cpp

@@ -14,38 +14,11 @@
 #include <anki/util/File.h>
 #include <anki/util/Filesystem.h>
 #include <anki/misc/Xml.h>
-#include <anki/renderer/Ms.h>
 #include <algorithm>
 
 namespace anki
 {
 
-/// Visit the textures to bind them
-class UpdateTexturesVisitor
-{
-public:
-	ResourceGroupInitInfo* m_init = nullptr;
-	ResourceManager* m_manager = nullptr;
-
-	template<typename TMaterialVariableTemplate>
-	Error visit(const TMaterialVariableTemplate& var)
-	{
-		// Do nothing
-		return ErrorCode::NONE;
-	}
-};
-
-// Specialize for texture
-template<>
-Error UpdateTexturesVisitor::visit<MaterialVariableTemplate<TextureResourcePtr>>(
-	const MaterialVariableTemplate<TextureResourcePtr>& var)
-{
-	m_init->m_textures[var.getTextureUnit()].m_texture = var.getValue()->getGrTexture();
-	m_init->m_textures[var.getTextureUnit()].m_usage =
-		TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::SAMPLED_TESSELLATION_EVALUATION;
-	return ErrorCode::NONE;
-}
-
 template<typename T>
 Error MaterialVariableTemplate<T>::init(U idx, const MaterialLoader::Input& in, Material& mtl)
 {
@@ -174,6 +147,7 @@ Error MaterialVariant::init(const RenderingKey& key2, Material& mtl, MaterialLoa
 	//
 	// Shaders
 	//
+	Array<ShaderPtr, 5> shaders;
 	for(ShaderType stype = ShaderType::VERTEX; stype <= ShaderType::FRAGMENT; ++stype)
 	{
 		if(stype == ShaderType::GEOMETRY)
@@ -192,14 +166,22 @@ Error MaterialVariant::init(const RenderingKey& key2, Material& mtl, MaterialLoa
 		StringAuto filename(mtl.getTempAllocator());
 		ANKI_CHECK(mtl.createProgramSourceToCache(src, stype, filename));
 
-		ShaderResourcePtr& shader = m_shaders[U(stype)];
+		ShaderResourcePtr& shader = m_shaders[stype];
 
 		ANKI_CHECK(mtl.getManager().loadResource(filename.toCString(), shader));
 
+		shaders[stype] = shader->getGrShader();
+
 		// Update the hash
 		mtl.m_hash ^= computeHash(&src[0], src.getLength());
 	}
 
+	m_prog = mtl.getManager().getGrManager().newInstance<ShaderProgram>(shaders[ShaderType::VERTEX],
+		shaders[ShaderType::TESSELLATION_CONTROL],
+		shaders[ShaderType::TESSELLATION_EVALUATION],
+		shaders[ShaderType::GEOMETRY],
+		shaders[ShaderType::FRAGMENT]);
+
 	return ErrorCode::NONE;
 }
 
@@ -398,22 +380,6 @@ Error Material::createProgramSourceToCache(const String& source, ShaderType type
 	return ErrorCode::NONE;
 }
 
-void Material::fillResourceGroupInitInfo(ResourceGroupInitInfo& rcinit)
-{
-	rcinit.m_uniformBuffers[0].m_uploadedMemory = true;
-	rcinit.m_uniformBuffers[0].m_usage = BufferUsageBit::UNIFORM_FRAGMENT | BufferUsageBit::UNIFORM_VERTEX;
-
-	UpdateTexturesVisitor visitor;
-	visitor.m_init = &rcinit;
-	visitor.m_manager = &getManager();
-
-	for(const auto& var : m_vars)
-	{
-		Error err = var->acceptVisitor(visitor);
-		(void)err;
-	}
-}
-
 const MaterialVariant& Material::getVariant(const RenderingKey& key) const
 {
 	U lod = min<U>(m_lodCount - 1, key.m_lod);

+ 6 - 7
src/anki/resource/Material.h

@@ -169,9 +169,9 @@ public:
 
 	~MaterialVariant();
 
-	ShaderPtr getShader(ShaderType type) const
+	ShaderProgramPtr getShaderProgram() const
 	{
-		return m_shaders[U(type)]->getGrShader();
+		return m_prog;
 	}
 
 	U getDefaultBlockSize() const
@@ -187,11 +187,12 @@ public:
 	}
 
 private:
-	/// All shaders except compute and geometry.
+	/// All shaders except compute.
 	Array<ShaderResourcePtr, 5> m_shaders;
 	U32 m_shaderBlockSize = 0;
 	DynamicArray<ShaderVariableBlockInfo> m_blockInfo;
 	DynamicArray<Bool8> m_varActive;
+	ShaderProgramPtr m_prog;
 
 	ANKI_USE_RESULT Error init(const RenderingKey& key, Material& mtl, MaterialLoader& loader);
 
@@ -313,8 +314,6 @@ public:
 		return m_vars;
 	}
 
-	void fillResourceGroupInitInfo(ResourceGroupInitInfo& rcinit);
-
 	static U getInstanceGroupIdx(U instanceCount);
 
 private:
@@ -329,8 +328,8 @@ private:
 
 	DynamicArray<MaterialVariant> m_variants;
 
-	/// This is a matrix of variants. It holds indices to m_variants. If the
-	/// idx is MAX_U16 then the variant is not present
+	/// This is a matrix of variants. It holds indices to m_variants. If the idx is MAX_U16 then the variant is not
+	/// present.
 	Array4d<U16, U(Pass::COUNT), MAX_LODS, 2, MAX_INSTANCE_GROUPS> m_variantMatrix;
 
 	DynamicArray<MaterialVariable*> m_vars;

+ 34 - 64
src/anki/resource/Model.cpp

@@ -31,74 +31,54 @@ void ModelPatch::getRenderingDataSub(
 	RenderingKey meshKey = key;
 	meshKey.m_lod = min<U>(key.m_lod, m_meshCount - 1);
 	const Mesh& mesh = getMesh(meshKey);
-	inf.m_resourceGroup = m_grResources[meshKey.m_lod];
 
-	// Get shaders
+	// Get program
 	{
 		RenderingKey mtlKey = key;
 		mtlKey.m_lod = min<U>(key.m_lod, m_mtl->getLodCount() - 1);
 
-		if(mtlKey.m_tessellation && !m_mtl->getTessellationEnabled())
-		{
-			ANKI_ASSERT(0);
-		}
-
-		if(mtlKey.m_pass == Pass::SM && !m_mtl->getShadowEnabled())
-		{
-			ANKI_ASSERT(0);
-		}
-
-		if(mtlKey.m_instanceCount > 1 && !m_mtl->isInstanced())
-		{
-			ANKI_ASSERT(0);
-		}
-
 		const MaterialVariant& variant = m_mtl->getVariant(mtlKey);
 
-		inf.m_state.m_shaders[ShaderType::VERTEX] = variant.getShader(ShaderType::VERTEX);
-
-		if(mtlKey.m_tessellation)
-		{
-			inf.m_state.m_shaders[ShaderType::TESSELLATION_CONTROL] =
-				variant.getShader(ShaderType::TESSELLATION_CONTROL);
-
-			inf.m_state.m_shaders[ShaderType::TESSELLATION_EVALUATION] =
-				variant.getShader(ShaderType::TESSELLATION_EVALUATION);
-		}
-
-		inf.m_state.m_shaders[ShaderType::FRAGMENT] = variant.getShader(ShaderType::FRAGMENT);
+		inf.m_program = variant.getShaderProgram();
 	}
 
-	// Vertex
-	VertexStateInfo& vert = inf.m_state.m_vertex;
-	vert.m_bindingCount = 1;
-	vert.m_attributeCount = 4;
-	vert.m_bindings[0].m_stride = sizeof(Vec3) + sizeof(HVec2) + 2 * sizeof(U32);
+	// Vertex info
+	{
+		inf.m_vertexBufferBindingCount = 1;
+		VertexBufferBinding& vertBuffBinding = inf.m_vertexBufferBindings[0];
+		vertBuffBinding.m_buffer = mesh.getVertexBuffer();
+		vertBuffBinding.m_offset = 0;
+		vertBuffBinding.m_stride = sizeof(Vec3) + sizeof(HVec2) + 2 * sizeof(U32);
 
-	vert.m_attributes[0].m_format = PixelFormat(ComponentFormat::R32G32B32, TransformFormat::FLOAT);
-	vert.m_attributes[0].m_offset = 0;
+		inf.m_vertexAttributeCount = 4;
+		auto& attribs = inf.m_vertexAttributes;
 
-	vert.m_attributes[1].m_format = PixelFormat(ComponentFormat::R16G16, TransformFormat::FLOAT);
-	vert.m_attributes[1].m_offset = sizeof(Vec3);
+		attribs[0].m_bufferBinding = 0;
+		attribs[0].m_format = PixelFormat(ComponentFormat::R32G32B32, TransformFormat::FLOAT);
+		attribs[0].m_relativeOffset = 0;
 
-	if(key.m_pass == Pass::MS_FS)
-	{
-		vert.m_attributes[2].m_format = PixelFormat(ComponentFormat::R10G10B10A2, TransformFormat::SNORM);
-		vert.m_attributes[2].m_offset = sizeof(Vec3) + sizeof(U32);
+		attribs[1].m_bufferBinding = 0;
+		attribs[1].m_format = PixelFormat(ComponentFormat::R16G16, TransformFormat::FLOAT);
+		attribs[1].m_relativeOffset = sizeof(Vec3);
 
-		vert.m_attributes[3].m_format = PixelFormat(ComponentFormat::R10G10B10A2, TransformFormat::SNORM);
-		vert.m_attributes[3].m_offset = sizeof(Vec3) + sizeof(U32) * 2;
-	}
-	else
-	{
-		vert.m_attributeCount = 2;
-	}
+		if(key.m_pass == Pass::MS_FS)
+		{
+			attribs[2].m_bufferBinding = 0;
+			attribs[2].m_format = PixelFormat(ComponentFormat::R10G10B10A2, TransformFormat::SNORM);
+			attribs[2].m_relativeOffset = sizeof(Vec3) + sizeof(U32);
 
-	// Input assembly
-	inf.m_state.m_inputAssembler.m_topology = PrimitiveTopology::TRIANGLES;
-	inf.m_state.m_inputAssembler.m_primitiveRestartEnabled = false;
+			attribs[3].m_bufferBinding = 0;
+			attribs[3].m_format = PixelFormat(ComponentFormat::R10G10B10A2, TransformFormat::SNORM);
+			attribs[3].m_relativeOffset = sizeof(Vec3) + sizeof(U32) * 2;
+		}
+		else
+		{
+			inf.m_vertexAttributeCount = 2;
+		}
+	}
 
-	inf.m_stateMask = PipelineSubStateBit::VERTEX | PipelineSubStateBit::SHADERS | PipelineSubStateBit::INPUT_ASSEMBLER;
+	// Index buff
+	inf.m_indexBuffer = mesh.getIndexBuffer();
 
 	// Other
 	if(subMeshIndicesArray.getSize() == 0 || mesh.getSubMeshesCount() == 0)
@@ -125,11 +105,7 @@ Error ModelPatch::create(WeakArray<CString> meshFNames, const CString& mtlFName,
 	// Load material
 	ANKI_CHECK(manager->loadResource(mtlFName, m_mtl));
 
-	// Iterate material variables for textures
-	ResourceGroupInitInfo rcinit;
-	m_mtl->fillResourceGroupInitInfo(rcinit);
-
-	// Load meshes and update resource group
+	// Load meshes
 	m_meshCount = 0;
 	for(U i = 0; i < meshFNames.getSize(); i++)
 	{
@@ -142,12 +118,6 @@ Error ModelPatch::create(WeakArray<CString> meshFNames, const CString& mtlFName,
 			return ErrorCode::USER_DATA;
 		}
 
-		rcinit.m_vertexBuffers[0].m_buffer = m_meshes[i]->getVertexBuffer();
-		rcinit.m_indexBuffer.m_buffer = m_meshes[i]->getIndexBuffer();
-		rcinit.m_indexSize = 2;
-
-		m_grResources[i] = manager->getGrManager().newInstance<ResourceGroup>(rcinit);
-
 		++m_meshCount;
 	}
 

+ 43 - 9
src/anki/resource/Model.h

@@ -23,6 +23,42 @@ class PhysicsCollisionShape;
 /// @addtogroup resource
 /// @{
 
+class VertexBufferBinding
+{
+public:
+	BufferPtr m_buffer;
+	PtrSize m_offset;
+	PtrSize m_stride;
+
+	Bool operator==(const VertexBufferBinding& b) const
+	{
+		return m_buffer == b.m_buffer && m_offset == b.m_offset && m_stride == b.m_stride;
+	}
+
+	Bool operator!=(const VertexBufferBinding& b) const
+	{
+		return !(*this == b);
+	}
+};
+
+class VertexAttributeInfo
+{
+public:
+	U32 m_bufferBinding;
+	PixelFormat m_format;
+	PtrSize m_relativeOffset;
+
+	Bool operator==(const VertexAttributeInfo& b) const
+	{
+		return m_bufferBinding == b.m_bufferBinding && m_format == b.m_format && m_relativeOffset == b.m_relativeOffset;
+	}
+
+	Bool operator!=(const VertexAttributeInfo& b) const
+	{
+		return !(*this == b);
+	}
+};
+
 class ModelRenderingInfo
 {
 public:
@@ -30,14 +66,14 @@ public:
 	Array<PtrSize, MAX_SUB_DRAWCALLS> m_indicesOffsetArray;
 	U32 m_drawcallCount;
 
-	ResourceGroupPtr m_resourceGroup;
-	PipelineInitInfo& m_state;
-	PipelineSubStateBit m_stateMask = PipelineSubStateBit::NONE;
+	ShaderProgramPtr m_program;
 
-	ModelRenderingInfo(PipelineInitInfo& state)
-		: m_state(state)
-	{
-	}
+	Array<VertexBufferBinding, MAX_VERTEX_ATTRIBUTES> m_vertexBufferBindings;
+	U32 m_vertexBufferBindingCount;
+	Array<VertexAttributeInfo, MAX_VERTEX_ATTRIBUTES> m_vertexAttributes;
+	U32 m_vertexAttributeCount;
+
+	BufferPtr m_indexBuffer;
 };
 
 /// Model patch interface class. Its very important class and it binds the material with the mesh
@@ -92,8 +128,6 @@ private:
 	U8 m_meshCount = 0;
 	MaterialResourcePtr m_mtl;
 
-	Array<ResourceGroupPtr, MAX_LODS> m_grResources;
-
 	/// Return the maximum number of LODs
 	U getLodCount() const;
 };

+ 2 - 5
src/anki/resource/ParticleEmitterResource.cpp

@@ -8,7 +8,6 @@
 #include <anki/resource/Model.h>
 #include <anki/util/StringList.h>
 #include <anki/misc/Xml.h>
-#include <anki/renderer/Ms.h>
 #include <cstring>
 
 namespace anki
@@ -198,15 +197,13 @@ Error ParticleEmitterResource::load(const ResourceFilename& filename)
 	return ErrorCode::NONE;
 }
 
-void ParticleEmitterResource::getRenderingInfo(U lod, PipelineInitInfo& state, PipelineSubStateBit& stateMask) const
+void ParticleEmitterResource::getRenderingInfo(U lod, ShaderProgramPtr& prog) const
 {
 	lod = min<U>(lod, m_lodCount - 1);
 
 	RenderingKey key(Pass::MS_FS, lod, false, 1);
 	const MaterialVariant& variant = m_material->getVariant(key);
-	state.m_shaders[ShaderType::VERTEX] = variant.getShader(ShaderType::VERTEX);
-	state.m_shaders[ShaderType::FRAGMENT] = variant.getShader(ShaderType::FRAGMENT);
-	stateMask = PipelineSubStateBit::SHADERS;
+	prog = variant.getShaderProgram();
 }
 
 } // end namespace anki

+ 2 - 2
src/anki/resource/ParticleEmitterResource.h

@@ -116,8 +116,8 @@ public:
 		return *m_material;
 	}
 
-	/// Get shaders for rendering.
-	void getRenderingInfo(U lod, PipelineInitInfo& state, PipelineSubStateBit& stateMask) const;
+	/// Get program for rendering.
+	void getRenderingInfo(U lod, ShaderProgramPtr& prog) const;
 
 	/// Load it
 	ANKI_USE_RESULT Error load(const ResourceFilename& filename);

+ 1 - 1
src/anki/scene/CMakeLists.txt

@@ -3,4 +3,4 @@ file(GLOB_RECURSE ANKI_SCENE_HEADERS *.h)
 
 add_library(ankiscene ${ANKI_SCENE_SOURCES} ${ANKI_SCENE_HEADERS})
 
-target_link_libraries(ankiscene ankicore ankievent)
+target_link_libraries(ankiscene ankievent)

+ 0 - 8
src/anki/scene/LensFlareComponent.cpp

@@ -25,14 +25,6 @@ Error LensFlareComponent::init(const CString& textureFilename)
 	// Texture
 	ANKI_CHECK(getSceneGraph().getResourceManager().loadResource(textureFilename, m_tex));
 
-	// Resource group
-	ResourceGroupInitInfo rcInit;
-	rcInit.m_textures[0].m_texture = m_tex->getGrTexture();
-	rcInit.m_textures[0].m_usage = TextureUsageBit::SAMPLED_FRAGMENT;
-	rcInit.m_uniformBuffers[0].m_uploadedMemory = true;
-	rcInit.m_uniformBuffers[0].m_usage = BufferUsageBit::UNIFORM_FRAGMENT | BufferUsageBit::UNIFORM_VERTEX;
-	m_rcGroup = getSceneGraph().getGrManager().newInstance<ResourceGroup>(rcInit);
-
 	return ErrorCode::NONE;
 }
 

+ 0 - 7
src/anki/scene/LensFlareComponent.h

@@ -72,11 +72,6 @@ public:
 		return m_tex->getGrTexture();
 	}
 
-	const ResourceGroupPtr& getResourceGroup() const
-	{
-		return m_rcGroup;
-	}
-
 	/// @name SceneComponent virtuals
 	/// @{
 	Error update(SceneNode& node, F32 prevTime, F32 crntTime, Bool& updated) override
@@ -95,8 +90,6 @@ private:
 	Vec2 m_otherFlareSize = Vec2(1.0);
 
 	Vec4 m_worldPosition = Vec4(0.0);
-
-	ResourceGroupPtr m_rcGroup;
 };
 /// @}
 

+ 16 - 5
src/anki/scene/ModelNode.cpp

@@ -71,16 +71,27 @@ Error ModelPatchNode::buildRendering(const RenderingBuildInfoIn& in, RenderingBu
 	ANKI_ASSERT(m_modelPatch->getSubMeshesCount() == 0);
 
 	// State
-	ModelRenderingInfo modelInf(*out.m_state);
+	ModelRenderingInfo modelInf;
 	m_modelPatch->getRenderingDataSub(in.m_key, WeakArray<U8>(), modelInf);
-	ANKI_ASSERT(modelInf.m_stateMask
-		== (PipelineSubStateBit::VERTEX | PipelineSubStateBit::SHADERS | PipelineSubStateBit::INPUT_ASSEMBLER));
 
-	out.m_stateMask = modelInf.m_stateMask;
+	out.m_vertexBufferBindingCount = modelInf.m_vertexBufferBindingCount;
+	for(U i = 0; i < modelInf.m_vertexBufferBindingCount; ++i)
+	{
+		static_cast<VertexBufferBinding&>(out.m_vertexBufferBindings[i]) = modelInf.m_vertexBufferBindings[i];
+	}
+
+	out.m_vertexAttributeCount = modelInf.m_vertexAttributeCount;
+	for(U i = 0; i < modelInf.m_vertexAttributeCount; ++i)
+	{
+		out.m_vertexAttributes[i] = modelInf.m_vertexAttributes[i];
+	}
+
+	out.m_indexBuffer = modelInf.m_indexBuffer;
+
+	out.m_program = modelInf.m_program;
 
 	// Other
 	ANKI_ASSERT(modelInf.m_drawcallCount == 1 && "Cannot accept multi-draw");
-	out.m_resourceGroup = modelInf.m_resourceGroup;
 	out.m_drawcall.m_elements.m_count = modelInf.m_indicesCountArray[0];
 	out.m_drawcall.m_elements.m_instanceCount = in.m_key.m_instanceCount;
 	out.m_drawcall.m_elements.m_firstIndex = modelInf.m_indicesOffsetArray[0] / sizeof(U16);

+ 19 - 41
src/anki/scene/ParticleEmitter.cpp

@@ -272,20 +272,6 @@ Error ParticleEmitter::init(const CString& filename)
 	// Create the vertex buffer and object
 	m_vertBuffSize = m_maxNumOfParticles * VERTEX_SIZE;
 
-	GrManager& gr = getSceneGraph().getGrManager();
-
-	ResourceGroupInitInfo rcinit;
-	m_particleEmitterResource->getMaterial().fillResourceGroupInitInfo(rcinit);
-
-	for(U i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i)
-	{
-		m_vertBuffs[i] = gr.newInstance<Buffer>(m_vertBuffSize, BufferUsageBit::VERTEX, BufferMapAccessBit::WRITE);
-
-		rcinit.m_vertexBuffers[0].m_buffer = m_vertBuffs[i];
-
-		m_grGroups[i] = gr.newInstance<ResourceGroup>(rcinit);
-	}
-
 	return ErrorCode::NONE;
 }
 
@@ -298,36 +284,29 @@ Error ParticleEmitter::buildRendering(const RenderingBuildInfoIn& in, RenderingB
 		return ErrorCode::NONE;
 	}
 
-	U frame = (getGlobalTimestamp() % 3);
-
-	PipelineSubStateBit stateMask;
-	m_particleEmitterResource->getRenderingInfo(in.m_key.m_lod, *out.m_state, stateMask);
-	ANKI_ASSERT(stateMask == PipelineSubStateBit::SHADERS);
+	m_particleEmitterResource->getRenderingInfo(in.m_key.m_lod, out.m_program);
 
-	VertexStateInfo& vertState = out.m_state->m_vertex;
-	vertState.m_bindingCount = 1;
-	vertState.m_bindings[0].m_stride = VERTEX_SIZE;
-	vertState.m_attributeCount = 3;
-	vertState.m_attributes[0].m_format = PixelFormat(ComponentFormat::R32G32B32, TransformFormat::FLOAT);
-	vertState.m_attributes[0].m_offset = 0;
-	vertState.m_attributes[0].m_binding = 0;
-	vertState.m_attributes[1].m_format = PixelFormat(ComponentFormat::R32, TransformFormat::FLOAT);
-	vertState.m_attributes[1].m_offset = sizeof(Vec3);
-	vertState.m_attributes[1].m_binding = 0;
-	vertState.m_attributes[2].m_format = PixelFormat(ComponentFormat::R32, TransformFormat::FLOAT);
-	vertState.m_attributes[2].m_offset = sizeof(Vec3) + sizeof(F32);
-	vertState.m_attributes[2].m_binding = 0;
+	out.m_vertexBufferBindingCount = 1;
+	out.m_vertexBufferBindings[0].m_token = m_vertBuffToken;
+	out.m_vertexBufferBindings[0].m_stride = VERTEX_SIZE;
 
-	out.m_state->m_inputAssembler.m_topology = PrimitiveTopology::POINTS;
+	out.m_vertexAttributeCount = 3;
+	out.m_vertexAttributes[0].m_bufferBinding = 0;
+	out.m_vertexAttributes[0].m_format = PixelFormat(ComponentFormat::R32G32B32, TransformFormat::FLOAT);
+	out.m_vertexAttributes[0].m_relativeOffset = 0;
+	out.m_vertexAttributes[1].m_bufferBinding = 0;
+	out.m_vertexAttributes[1].m_format = PixelFormat(ComponentFormat::R32, TransformFormat::FLOAT);
+	out.m_vertexAttributes[1].m_relativeOffset = sizeof(Vec3);
+	out.m_vertexAttributes[2].m_bufferBinding = 0;
+	out.m_vertexAttributes[2].m_format = PixelFormat(ComponentFormat::R32, TransformFormat::FLOAT);
+	out.m_vertexAttributes[2].m_relativeOffset = sizeof(Vec3) + sizeof(F32);
 
-	out.m_stateMask = stateMask | PipelineSubStateBit::VERTEX | PipelineSubStateBit::INPUT_ASSEMBLER;
+	out.m_topology = PrimitiveTopology::POINTS;
 
 	out.m_drawArrays = true;
 	out.m_drawcall.m_arrays.m_count = m_aliveParticlesCount;
 
-	out.m_resourceGroup = m_grGroups[frame];
-
-	// The particles are already in world position
+	// The particles are already in world position but materials use the MVP
 	out.m_hasTransform = true;
 	out.m_transform = Mat4::getIdentity();
 
@@ -396,8 +375,9 @@ Error ParticleEmitter::frameUpdate(F32 prevUpdateTime, F32 crntTime)
 	Vec4 aabbmax(MIN_F32, MIN_F32, MIN_F32, 0.0);
 	m_aliveParticlesCount = 0;
 
-	U frame = getGlobalTimestamp() % 3;
-	F32* verts = static_cast<F32*>(m_vertBuffs[frame]->map(0, m_vertBuffSize, BufferMapAccessBit::WRITE));
+	F32* verts = static_cast<F32*>(getResourceManager().getGrManager().allocateFrameTransientMemory(
+		m_vertBuffSize, BufferUsageBit::VERTEX, m_vertBuffToken));
+
 	const F32* verts_base = verts;
 	(void)verts_base;
 
@@ -456,8 +436,6 @@ Error ParticleEmitter::frameUpdate(F32 prevUpdateTime, F32 crntTime)
 		}
 	}
 
-	m_vertBuffs[frame]->unmap();
-
 	if(m_aliveParticlesCount != 0)
 	{
 		Vec4 min = aabbmin - m_particle.m_size;

+ 1 - 2
src/anki/scene/ParticleEmitter.h

@@ -190,8 +190,7 @@ private:
 	/// @name Graphics
 	/// @{
 	U32 m_vertBuffSize = 0;
-	Array<BufferPtr, MAX_FRAMES_IN_FLIGHT> m_vertBuffs;
-	Array<ResourceGroupPtr, MAX_FRAMES_IN_FLIGHT> m_grGroups;
+	TransientMemoryToken m_vertBuffToken;
 	/// @}
 
 	SimulationType m_simulationType = SimulationType::UNDEFINED;

+ 0 - 2
src/anki/scene/RenderComponent.cpp

@@ -63,8 +63,6 @@ RenderComponent::~RenderComponent()
 	}
 
 	m_vars.destroy(alloc);
-
-	m_localPplineCache.destroy(getAllocator());
 }
 
 Error RenderComponent::init()

+ 61 - 45
src/anki/scene/RenderComponent.h

@@ -8,6 +8,7 @@
 #include <anki/scene/Common.h>
 #include <anki/scene/SceneComponent.h>
 #include <anki/resource/Material.h>
+#include <anki/resource/Model.h>
 #include <anki/util/HashMap.h>
 
 namespace anki
@@ -110,12 +111,49 @@ public:
 	U32 m_subMeshIndicesCount;
 };
 
+/// Info on the vertex buffer binding.
+class RenderingVertexBufferBinding : public VertexBufferBinding
+{
+public:
+	TransientMemoryToken m_token;
+
+	Bool operator==(const RenderingVertexBufferBinding& b) const
+	{
+		if(m_token)
+		{
+			return m_token == b.m_token;
+		}
+		else
+		{
+			return static_cast<const VertexBufferBinding&>(*this) == static_cast<const VertexBufferBinding&>(b);
+		}
+	}
+
+	Bool operator!=(const RenderingVertexBufferBinding& b) const
+	{
+		return !(*this == b);
+	}
+};
+
+using RenderingVertexAttributeInfo = VertexAttributeInfo;
+
 /// Rendering data output.
 class RenderingBuildInfoOut
 {
 public:
-	ResourceGroupPtr m_resourceGroup;
 	Mat4 m_transform;
+	Bool8 m_hasTransform = false;
+
+	ShaderProgramPtr m_program;
+
+	Array<RenderingVertexBufferBinding, MAX_VERTEX_ATTRIBUTES> m_vertexBufferBindings;
+	U32 m_vertexBufferBindingCount;
+	Array<RenderingVertexAttributeInfo, MAX_VERTEX_ATTRIBUTES> m_vertexAttributes;
+	U32 m_vertexAttributeCount;
+
+	BufferPtr m_indexBuffer;
+	TransientMemoryToken m_indexBufferToken;
+
 	union A
 	{
 		DrawArraysIndirectInfo m_arrays;
@@ -126,17 +164,11 @@ public:
 		{
 		}
 	} m_drawcall;
-	Bool8 m_drawArrays = false;
-	Bool8 m_hasTransform = false;
+	Bool m_drawArrays = false;
 
-	PipelineInitInfo* m_state = nullptr;
-	PipelineSubStateBit m_stateMask = PipelineSubStateBit::NONE;
+	PrimitiveTopology m_topology = PrimitiveTopology::TRIANGLES;
 
-	RenderingBuildInfoOut(PipelineInitInfo* state)
-		: m_state(state)
-	{
-		ANKI_ASSERT(state);
-	}
+	RenderingBuildInfoOut() = default;
 
 	RenderingBuildInfoOut(const RenderingBuildInfoOut& b)
 	{
@@ -145,13 +177,27 @@ public:
 
 	RenderingBuildInfoOut& operator=(const RenderingBuildInfoOut& b)
 	{
-		m_resourceGroup = b.m_resourceGroup;
 		m_transform = b.m_transform;
-		m_drawcall.m_elements = b.m_drawcall.m_elements;
-		m_drawArrays = b.m_drawArrays;
 		m_hasTransform = b.m_hasTransform;
-		m_state = b.m_state;
-		m_stateMask = b.m_stateMask;
+
+		m_vertexBufferBindingCount = b.m_vertexBufferBindingCount;
+		for(U i = 0; i < m_vertexBufferBindingCount; ++i)
+		{
+			m_vertexBufferBindings[i] = b.m_vertexBufferBindings[i];
+		}
+
+		m_vertexAttributeCount = b.m_vertexAttributeCount;
+		for(U i = 0; i < m_vertexAttributeCount; ++i)
+		{
+			m_vertexAttributes[i] = b.m_vertexAttributes[i];
+		}
+
+		m_indexBuffer = b.m_indexBuffer;
+		m_indexBufferToken = b.m_indexBufferToken;
+
+		m_drawArrays = b.m_drawArrays;
+
+		m_topology = b.m_topology;
 
 		return *this;
 	}
@@ -221,32 +267,6 @@ public:
 		return err;
 	}
 
-	Bool tryGetPipeline(U64 hash, PipelinePtr& ppline)
-	{
-		LockGuard<SpinLock> lock(m_localPplineCacheMtx);
-
-		auto it = m_localPplineCache.find(hash);
-		if(it != m_localPplineCache.getEnd())
-		{
-			ppline = *it;
-			return true;
-		}
-		else
-		{
-			return false;
-		}
-	}
-
-	void storePipeline(U64 hash, PipelinePtr ppline)
-	{
-		LockGuard<SpinLock> lock(m_localPplineCacheMtx);
-
-		if(m_localPplineCache.find(hash) == m_localPplineCache.getEnd())
-		{
-			m_localPplineCache.pushBack(getAllocator(), hash, ppline);
-		}
-	}
-
 private:
 	using Key = U64;
 
@@ -272,10 +292,6 @@ private:
 
 	Variables m_vars;
 	const Material* m_mtl;
-
-	/// This is an optimization, a local hash of pipelines.
-	HashMap<U64, PipelinePtr, Hasher, Compare> m_localPplineCache;
-	SpinLock m_localPplineCacheMtx;
 };
 /// @}
 

+ 2 - 2
src/anki/scene/SoftwareRasterizer.cpp

@@ -17,11 +17,11 @@ void SoftwareRasterizer::prepare(const Mat4& mv, const Mat4& p, U width, U heigh
 	m_mvp = p * mv;
 
 	Array<Plane*, 6> planes = {
-		&m_planesL[0], &m_planesL[1], &m_planesL[2], &m_planesL[3], &m_planesL[4], &m_planesL[5]};
+		{&m_planesL[0], &m_planesL[1], &m_planesL[2], &m_planesL[3], &m_planesL[4], &m_planesL[5]}};
 	extractClipPlanes(p, planes);
 
 	Array<Plane*, 6> planes2 = {
-		&m_planesW[0], &m_planesW[1], &m_planesW[2], &m_planesW[3], &m_planesW[4], &m_planesW[5]};
+		{&m_planesW[0], &m_planesW[1], &m_planesW[2], &m_planesW[3], &m_planesW[4], &m_planesW[5]}};
 	extractClipPlanes(m_mvp, planes2);
 
 	// Reset z buffer

+ 9 - 5
src/anki/ui/UiInterfaceImpl.cpp

@@ -33,7 +33,8 @@ Error UiInterfaceImpl::init(GrManager* gr, ResourceManager* rc)
 
 	ANKI_CHECK(rc->loadResource("shaders/UiLines.frag.glsl", m_stages[StageId::LINES].m_fShader));
 
-	// Init pplines
+// Init pplines
+#if 0
 	PipelineInitInfo ppinit;
 	ppinit.m_vertex.m_bindingCount = 1;
 	ppinit.m_vertex.m_bindings[0].m_stride = sizeof(Vertex);
@@ -58,6 +59,7 @@ Error UiInterfaceImpl::init(GrManager* gr, ResourceManager* rc)
 	ppinit.m_shaders[U(ShaderType::VERTEX)] = m_stages[StageId::LINES].m_vShader->getGrShader();
 	ppinit.m_shaders[U(ShaderType::FRAGMENT)] = m_stages[StageId::LINES].m_fShader->getGrShader();
 	m_stages[StageId::LINES].m_ppline = gr->newInstance<Pipeline>(ppinit);
+#endif
 
 	// Init buffers
 	for(U s = 0; s < StageId::COUNT; ++s)
@@ -69,7 +71,8 @@ Error UiInterfaceImpl::init(GrManager* gr, ResourceManager* rc)
 		}
 	}
 
-	// Init resource groups
+// Init resource groups
+#if 0
 	for(U s = 0; s < StageId::COUNT; ++s)
 	{
 		for(U i = 0; i < m_stages[s].m_rcGroups.getSize(); ++i)
@@ -85,6 +88,7 @@ Error UiInterfaceImpl::init(GrManager* gr, ResourceManager* rc)
 			m_stages[s].m_rcGroups[i] = gr->newInstance<ResourceGroup>(rcinit);
 		}
 	}
+#endif
 
 	return ErrorCode::NONE;
 }
@@ -123,9 +127,9 @@ void UiInterfaceImpl::drawLines(const WeakArray<UVec2>& positions, const Color&
 
 	ANKI_ASSERT(m_vertCounts[stageId] + positions.getSize() <= MAX_VERTS);
 
-	m_cmdb->bindPipeline(m_stages[StageId::LINES].m_ppline);
-	m_cmdb->bindResourceGroup(m_stages[StageId::LINES].m_rcGroups[m_timestamp], 0, nullptr);
-	m_cmdb->drawArrays(positions.getSize(), 1, m_vertCounts[stageId]);
+	// m_cmdb->bindPipeline(m_stages[StageId::LINES].m_ppline);
+	// m_cmdb->bindResourceGroup(m_stages[StageId::LINES].m_rcGroups[m_timestamp], 0, nullptr);
+	// m_cmdb->drawArrays(positions.getSize(), 1, m_vertCounts[stageId]);
 
 	for(const UVec2& pos : positions)
 	{

+ 0 - 2
src/anki/ui/UiInterfaceImpl.h

@@ -75,9 +75,7 @@ private:
 	public:
 		ShaderResourcePtr m_vShader;
 		ShaderResourcePtr m_fShader;
-		PipelinePtr m_ppline;
 		Array<BufferPtr, MAX_FRAMES_IN_FLIGHT> m_vertBuffs;
-		Array<ResourceGroupPtr, MAX_FRAMES_IN_FLIGHT> m_rcGroups;
 	};
 
 	Array<Stage, StageId::COUNT> m_stages;

+ 18 - 0
src/anki/util/Thread.h

@@ -181,6 +181,24 @@ public:
 	/// Wait until all threads call wait().
 	Bool wait();
 
+private:
+	void* m_impl = nullptr;
+};
+
+/// Semaphore for thread synchronization.
+class Semaphore : public NonCopyable
+{
+public:
+	Semaphore(I32 initialValue);
+
+	~Semaphore();
+
+	/// Same as sem_wait().
+	void wait();
+
+	/// Same as sem_post().
+	void post();
+
 private:
 	void* m_impl = nullptr;
 };

+ 49 - 0
src/anki/util/ThreadPosix.cpp

@@ -8,6 +8,7 @@
 #include <cstring>
 #include <algorithm>
 #include <pthread.h>
+#include <semaphore.h>
 
 namespace anki
 {
@@ -300,4 +301,52 @@ Bool Barrier::wait()
 	return true;
 }
 
+Semaphore::Semaphore(I32 val)
+{
+	sem_t* sem = static_cast<sem_t*>(malloc(sizeof(sem_t)));
+	m_impl = sem;
+	if(m_impl == nullptr)
+	{
+		ANKI_LOGF("Out of memory");
+	}
+
+	if(sem_init(sem, 0, val))
+	{
+		ANKI_LOGF("sem_init() failed");
+	}
+}
+
+Semaphore::~Semaphore()
+{
+	if(m_impl)
+	{
+		sem_t* sem = static_cast<sem_t*>(m_impl);
+		if(sem_destroy(sem))
+		{
+			ANKI_LOGE("sem_destroy() failed");
+		}
+
+		free(m_impl);
+		m_impl = nullptr;
+	}
+}
+
+void Semaphore::wait()
+{
+	ANKI_ASSERT(m_impl);
+	if(ANKI_UNLIKELY(sem_wait(static_cast<sem_t*>(m_impl))))
+	{
+		ANKI_LOGF("sem_wait() failed");
+	}
+}
+
+void Semaphore::post()
+{
+	ANKI_ASSERT(m_impl);
+	if(ANKI_UNLIKELY(sem_post(static_cast<sem_t*>(m_impl))))
+	{
+		ANKI_LOGF("sem_post() failed");
+	}
+}
+
 } // end namespace anki

+ 96 - 208
tests/gr/Gr.cpp

@@ -298,20 +298,11 @@ static void createGrManager(NativeWindow*& win, GrManager*& gr)
 	ANKI_TEST_EXPECT_NO_ERR(gr->init(inf));
 }
 
-static PipelinePtr createSimplePpline(CString vertSrc, CString fragSrc, GrManager& gr)
+static ShaderProgramPtr createProgram(CString vertSrc, CString fragSrc, GrManager& gr)
 {
 	ShaderPtr vert = gr.newInstance<Shader>(ShaderType::VERTEX, vertSrc);
 	ShaderPtr frag = gr.newInstance<Shader>(ShaderType::FRAGMENT, fragSrc);
-
-	PipelineInitInfo init;
-	init.m_shaders[ShaderType::VERTEX] = vert;
-	init.m_shaders[ShaderType::FRAGMENT] = frag;
-	init.m_color.m_attachments[0].m_format.m_components = ComponentFormat::DEFAULT_FRAMEBUFFER;
-	init.m_color.m_attachmentCount = 1;
-	init.m_depthStencil.m_depthWriteEnabled = false;
-	init.m_depthStencil.m_depthCompareFunction = CompareOperation::ALWAYS;
-
-	return gr.newInstance<Pipeline>(init);
+	return gr.newInstance<ShaderProgram>(vert, frag);
 }
 
 static FramebufferPtr createDefaultFb(GrManager& gr)
@@ -358,11 +349,11 @@ ANKI_TEST(Gr, Shader)
 	COMMON_END()
 }
 
-ANKI_TEST(Gr, Pipeline)
+ANKI_TEST(Gr, ShaderProgram)
 {
 	COMMON_BEGIN()
 
-	PipelinePtr ppline = createSimplePpline(VERT_SRC, FRAG_SRC, *gr);
+	ShaderProgramPtr ppline = createProgram(VERT_SRC, FRAG_SRC, *gr);
 
 	COMMON_END()
 }
@@ -371,7 +362,7 @@ ANKI_TEST(Gr, SimpleDrawcall)
 {
 	COMMON_BEGIN()
 
-	PipelinePtr ppline = createSimplePpline(VERT_SRC, FRAG_SRC, *gr);
+	ShaderProgramPtr prog = createProgram(VERT_SRC, FRAG_SRC, *gr);
 	FramebufferPtr fb = createDefaultFb(*gr);
 
 	U iterations = 100;
@@ -383,13 +374,13 @@ ANKI_TEST(Gr, SimpleDrawcall)
 		gr->beginFrame();
 
 		CommandBufferInitInfo cinit;
+		cinit.m_flags = CommandBufferFlag::GRAPHICS_WORK;
 		CommandBufferPtr cmdb = gr->newInstance<CommandBuffer>(cinit);
 
 		cmdb->setViewport(0, 0, WIDTH, HEIGHT);
-		cmdb->setPolygonOffset(0.0, 0.0);
-		cmdb->bindPipeline(ppline);
+		cmdb->bindShaderProgram(prog);
 		cmdb->beginRenderPass(fb);
-		cmdb->drawArrays(3);
+		cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 3);
 		cmdb->endRenderPass();
 		cmdb->flush();
 
@@ -430,20 +421,6 @@ ANKI_TEST(Gr, Buffer)
 	COMMON_END()
 }
 
-ANKI_TEST(Gr, ResourceGroup)
-{
-	COMMON_BEGIN()
-
-	BufferPtr b = gr->newInstance<Buffer>(sizeof(F32) * 4, BufferUsageBit::UNIFORM_ALL, BufferMapAccessBit::WRITE);
-
-	ResourceGroupInitInfo rcinit;
-	rcinit.m_uniformBuffers[0].m_buffer = b;
-	rcinit.m_uniformBuffers[0].m_usage = BufferUsageBit::UNIFORM_ALL_GRAPHICS;
-	ResourceGroupPtr rc = gr->newInstance<ResourceGroup>(rcinit);
-
-	COMMON_END()
-}
-
 ANKI_TEST(Gr, DrawWithUniforms)
 {
 	COMMON_BEGIN()
@@ -458,16 +435,8 @@ ANKI_TEST(Gr, DrawWithUniforms)
 	ptr[2] = Vec4(0.0, 0.0, 1.0, 0.0);
 	b->unmap();
 
-	// Resource group
-	ResourceGroupInitInfo rcinit;
-	rcinit.m_uniformBuffers[0].m_buffer = b;
-	rcinit.m_uniformBuffers[0].m_usage = BufferUsageBit::UNIFORM_ALL_GRAPHICS;
-	rcinit.m_uniformBuffers[1].m_uploadedMemory = true;
-	rcinit.m_uniformBuffers[1].m_usage = BufferUsageBit::UNIFORM_ALL_GRAPHICS;
-	ResourceGroupPtr rc = gr->newInstance<ResourceGroup>(rcinit);
-
-	// Ppline
-	PipelinePtr ppline = createSimplePpline(VERT_UBO_SRC, FRAG_UBO_SRC, *gr);
+	// Progm
+	ShaderProgramPtr prog = createProgram(VERT_UBO_SRC, FRAG_UBO_SRC, *gr);
 
 	// FB
 	FramebufferPtr fb = createDefaultFb(*gr);
@@ -481,9 +450,9 @@ ANKI_TEST(Gr, DrawWithUniforms)
 		gr->beginFrame();
 
 		// Uploaded buffer
-		TransientMemoryInfo transientInfo;
-		Vec4* rotMat = static_cast<Vec4*>(gr->allocateFrameTransientMemory(
-			sizeof(Vec4), BufferUsageBit::UNIFORM_ALL, transientInfo.m_uniformBuffers[1]));
+		TransientMemoryToken token;
+		Vec4* rotMat =
+			static_cast<Vec4*>(gr->allocateFrameTransientMemory(sizeof(Vec4), BufferUsageBit::UNIFORM_ALL, token));
 		F32 angle = toRad(360.0f / ITERATION_COUNT * iterations);
 		(*rotMat)[0] = cos(angle);
 		(*rotMat)[1] = -sin(angle);
@@ -494,11 +463,13 @@ ANKI_TEST(Gr, DrawWithUniforms)
 		CommandBufferPtr cmdb = gr->newInstance<CommandBuffer>(cinit);
 
 		cmdb->setViewport(0, 0, WIDTH, HEIGHT);
-		cmdb->setPolygonOffset(0.0, 0.0);
-		cmdb->bindPipeline(ppline);
+		cmdb->bindShaderProgram(prog);
 		cmdb->beginRenderPass(fb);
-		cmdb->bindResourceGroup(rc, 0, &transientInfo);
-		cmdb->drawArrays(3);
+
+		cmdb->bindUniformBuffer(0, 0, b, 0);
+		cmdb->bindUniformBuffer(0, 1, token);
+
+		cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 3);
 		cmdb->endRenderPass();
 		cmdb->flush();
 
@@ -550,37 +521,8 @@ ANKI_TEST(Gr, DrawWithVertex)
 	otherColor[2] = Vec3(1.0, 1.0, 0.0);
 	c->unmap();
 
-	// Resource group
-	ResourceGroupInitInfo rcinit;
-	rcinit.m_vertexBuffers[0].m_buffer = b;
-	rcinit.m_vertexBuffers[1].m_buffer = c;
-	ResourceGroupPtr rc = gr->newInstance<ResourceGroup>(rcinit);
-
-	// Shaders
-	ShaderPtr vert = gr->newInstance<Shader>(ShaderType::VERTEX, VERT_INP_SRC);
-	ShaderPtr frag = gr->newInstance<Shader>(ShaderType::FRAGMENT, FRAG_INP_SRC);
-
-	// Ppline
-	PipelineInitInfo init;
-	init.m_shaders[ShaderType::VERTEX] = vert;
-	init.m_shaders[ShaderType::FRAGMENT] = frag;
-	init.m_color.m_attachments[0].m_format.m_components = ComponentFormat::DEFAULT_FRAMEBUFFER;
-	init.m_color.m_attachmentCount = 1;
-	init.m_depthStencil.m_depthWriteEnabled = false;
-	init.m_depthStencil.m_depthCompareFunction = CompareOperation::ALWAYS;
-
-	init.m_vertex.m_attributeCount = 3;
-	init.m_vertex.m_attributes[0].m_format = PixelFormat(ComponentFormat::R32G32B32, TransformFormat::FLOAT);
-	init.m_vertex.m_attributes[1].m_format = PixelFormat(ComponentFormat::R8G8B8, TransformFormat::UNORM);
-	init.m_vertex.m_attributes[1].m_offset = sizeof(Vec3);
-	init.m_vertex.m_attributes[2].m_format = PixelFormat(ComponentFormat::R32G32B32, TransformFormat::FLOAT);
-	init.m_vertex.m_attributes[2].m_binding = 1;
-
-	init.m_vertex.m_bindingCount = 2;
-	init.m_vertex.m_bindings[0].m_stride = sizeof(Vert);
-	init.m_vertex.m_bindings[1].m_stride = sizeof(Vec3);
-
-	PipelinePtr ppline = gr->newInstance<Pipeline>(init);
+	// Prog
+	ShaderProgramPtr prog = createProgram(VERT_INP_SRC, FRAG_INP_SRC, *gr);
 
 	// FB
 	FramebufferPtr fb = createDefaultFb(*gr);
@@ -594,14 +536,20 @@ ANKI_TEST(Gr, DrawWithVertex)
 		gr->beginFrame();
 
 		CommandBufferInitInfo cinit;
+		cinit.m_flags = CommandBufferFlag::GRAPHICS_WORK;
 		CommandBufferPtr cmdb = gr->newInstance<CommandBuffer>(cinit);
 
+		cmdb->bindVertexBuffer(0, b, 0, sizeof(Vert));
+		cmdb->bindVertexBuffer(1, c, 0, sizeof(Vec3));
+		cmdb->setVertexAttribute(0, 0, PixelFormat(ComponentFormat::R32G32B32, TransformFormat::FLOAT), 0);
+		cmdb->setVertexAttribute(1, 0, PixelFormat(ComponentFormat::R8G8B8, TransformFormat::UNORM), sizeof(Vec3));
+		cmdb->setVertexAttribute(2, 1, PixelFormat(ComponentFormat::R32G32B32, TransformFormat::FLOAT), 0);
+
 		cmdb->setViewport(0, 0, WIDTH, HEIGHT);
 		cmdb->setPolygonOffset(0.0, 0.0);
-		cmdb->bindPipeline(ppline);
+		cmdb->bindShaderProgram(prog);
 		cmdb->beginRenderPass(fb);
-		cmdb->bindResourceGroup(rc, 0, nullptr);
-		cmdb->drawArrays(3);
+		cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 3);
 		cmdb->endRenderPass();
 		cmdb->flush();
 
@@ -756,7 +704,6 @@ ANKI_TEST(Gr, DrawWithTexture)
 		a, TextureUsageBit::SAMPLED_FRAGMENT, TextureUsageBit::UPLOAD, TextureSurfaceInfo(1, 0, 0, 0));
 
 	cmdb->setTextureSurfaceBarrier(b, TextureUsageBit::NONE, TextureUsageBit::UPLOAD, TextureSurfaceInfo(0, 0, 0, 0));
-	;
 
 	cmdb->uploadTextureSurfaceCopyData(a, TextureSurfaceInfo(0, 0, 0, 0), &mip0[0], sizeof(mip0));
 
@@ -786,17 +733,9 @@ ANKI_TEST(Gr, DrawWithTexture)
 	cmdb->flush();
 
 	//
-	// Create resource group
-	//
-	ResourceGroupInitInfo rcinit;
-	rcinit.m_textures[0].m_texture = a;
-	rcinit.m_textures[1].m_texture = b;
-	ResourceGroupPtr rc = gr->newInstance<ResourceGroup>(rcinit);
-
-	//
-	// Create ppline
+	// Create prog
 	//
-	PipelinePtr ppline = createSimplePpline(VERT_QUAD_SRC, FRAG_TEX_SRC, *gr);
+	ShaderProgramPtr prog = createProgram(VERT_QUAD_SRC, FRAG_TEX_SRC, *gr);
 
 	//
 	// Create FB
@@ -816,14 +755,15 @@ ANKI_TEST(Gr, DrawWithTexture)
 		gr->beginFrame();
 
 		CommandBufferInitInfo cinit;
+		cinit.m_flags = CommandBufferFlag::GRAPHICS_WORK;
 		CommandBufferPtr cmdb = gr->newInstance<CommandBuffer>(cinit);
 
 		cmdb->setViewport(0, 0, WIDTH, HEIGHT);
-		cmdb->setPolygonOffset(0.0, 0.0);
-		cmdb->bindPipeline(ppline);
+		cmdb->bindShaderProgram(prog);
 		cmdb->beginRenderPass(fb);
-		cmdb->bindResourceGroup(rc, 0, nullptr);
-		cmdb->drawArrays(6);
+		cmdb->bindTexture(0, 0, a);
+		cmdb->bindTexture(0, 1, b);
+		cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 6);
 		cmdb->endRenderPass();
 		cmdb->flush();
 
@@ -840,8 +780,12 @@ ANKI_TEST(Gr, DrawWithTexture)
 	COMMON_END()
 }
 
-static void drawOffscreenDrawcalls(
-	GrManager& gr, PipelinePtr ppline, ResourceGroupPtr rc0, CommandBufferPtr cmdb, U viewPortSize)
+static void drawOffscreenDrawcalls(GrManager& gr,
+	ShaderProgramPtr prog,
+	CommandBufferPtr cmdb,
+	U viewPortSize,
+	BufferPtr indexBuff,
+	BufferPtr vertBuff)
 {
 	static F32 ang = -2.5f;
 	ang += toRad(2.5f);
@@ -851,38 +795,41 @@ static void drawOffscreenDrawcalls(
 
 	Mat4 projMat = Mat4::calculatePerspectiveProjectionMatrix(toRad(60.0), toRad(60.0), 0.1f, 100.0f);
 
-	TransientMemoryInfo transientInfo;
+	TransientMemoryToken token0, token1;
 
 	Mat4 modelMat(Vec4(-0.5, -0.5, 0.0, 1.0), Mat3(Euler(ang, ang / 2.0f, ang / 3.0f)), 1.0f);
 
-	Mat4* mvp = static_cast<Mat4*>(
-		gr.allocateFrameTransientMemory(sizeof(*mvp), BufferUsageBit::UNIFORM_ALL, transientInfo.m_uniformBuffers[0]));
+	Mat4* mvp = static_cast<Mat4*>(gr.allocateFrameTransientMemory(sizeof(*mvp), BufferUsageBit::UNIFORM_ALL, token0));
 	*mvp = projMat * viewMat * modelMat;
 
-	Vec4* color = static_cast<Vec4*>(gr.allocateFrameTransientMemory(
-		sizeof(*color) * 2, BufferUsageBit::UNIFORM_ALL, transientInfo.m_uniformBuffers[1]));
+	Vec4* color =
+		static_cast<Vec4*>(gr.allocateFrameTransientMemory(sizeof(*color) * 2, BufferUsageBit::UNIFORM_ALL, token1));
 	*color++ = Vec4(1.0, 0.0, 0.0, 0.0);
 	*color = Vec4(0.0, 1.0, 0.0, 0.0);
 
-	cmdb->bindPipeline(ppline);
+	cmdb->bindVertexBuffer(0, vertBuff, 0, sizeof(Vec3));
+	cmdb->setVertexAttribute(0, 0, PixelFormat(ComponentFormat::R32G32B32, TransformFormat::FLOAT), 0);
+	cmdb->bindShaderProgram(prog);
+	cmdb->bindIndexBuffer(indexBuff, 0, IndexType::U16);
 	cmdb->setViewport(0, 0, viewPortSize, viewPortSize);
-	cmdb->bindResourceGroup(rc0, 0, &transientInfo);
-	cmdb->drawElements(6 * 2 * 3);
+	cmdb->bindUniformBuffer(0, 0, token0);
+	cmdb->bindUniformBuffer(0, 1, token1);
+	cmdb->drawElements(PrimitiveTopology::TRIANGLES, 6 * 2 * 3);
 
 	// 2nd draw
 	modelMat = Mat4(Vec4(0.5, 0.5, 0.0, 1.0), Mat3(Euler(ang * 2.0, ang, ang / 3.0f * 2.0)), 1.0f);
 
-	mvp = static_cast<Mat4*>(
-		gr.allocateFrameTransientMemory(sizeof(*mvp), BufferUsageBit::UNIFORM_ALL, transientInfo.m_uniformBuffers[0]));
+	mvp = static_cast<Mat4*>(gr.allocateFrameTransientMemory(sizeof(*mvp), BufferUsageBit::UNIFORM_ALL, token0));
 	*mvp = projMat * viewMat * modelMat;
 
-	color = static_cast<Vec4*>(gr.allocateFrameTransientMemory(
-		sizeof(*color) * 2, BufferUsageBit::UNIFORM_ALL, transientInfo.m_uniformBuffers[1]));
+	color =
+		static_cast<Vec4*>(gr.allocateFrameTransientMemory(sizeof(*color) * 2, BufferUsageBit::UNIFORM_ALL, token1));
 	*color++ = Vec4(0.0, 0.0, 1.0, 0.0);
 	*color = Vec4(0.0, 1.0, 1.0, 0.0);
 
-	cmdb->bindResourceGroup(rc0, 0, &transientInfo);
-	cmdb->drawElements(6 * 2 * 3);
+	cmdb->bindUniformBuffer(0, 0, token0);
+	cmdb->bindUniformBuffer(0, 1, token1);
+	cmdb->drawElements(PrimitiveTopology::TRIANGLES, 6 * 2 * 3);
 }
 
 static void drawOffscreen(GrManager& gr, Bool useSecondLevel)
@@ -923,6 +870,7 @@ static void drawOffscreen(GrManager& gr, Bool useSecondLevel)
 	fbinit.m_colorAttachments[1].m_texture = col1;
 	fbinit.m_colorAttachments[1].m_clearValue.m_colorf = {0.0, 0.1, 0.0, 0.0};
 	fbinit.m_depthStencilAttachment.m_texture = dp;
+	fbinit.m_depthStencilAttachment.m_aspect = DepthStencilAspectMask::DEPTH;
 	fbinit.m_depthStencilAttachment.m_clearValue.m_depthStencil.m_depth = 1.0;
 
 	FramebufferPtr fb = gr.newInstance<Framebuffer>(fbinit);
@@ -933,51 +881,16 @@ static void drawOffscreen(GrManager& gr, Bool useSecondLevel)
 	FramebufferPtr dfb = createDefaultFb(gr);
 
 	//
-	// Create buffs and rc groups
+	// Create buffs
 	//
 	BufferPtr verts, indices;
 	createCube(gr, verts, indices);
 
-	ResourceGroupInitInfo rcinit;
-	rcinit.m_uniformBuffers[0].m_uploadedMemory = true;
-	rcinit.m_uniformBuffers[0].m_usage = BufferUsageBit::UNIFORM_FRAGMENT | BufferUsageBit::UNIFORM_VERTEX;
-	rcinit.m_uniformBuffers[1].m_uploadedMemory = true;
-	rcinit.m_uniformBuffers[1].m_usage = BufferUsageBit::UNIFORM_FRAGMENT | BufferUsageBit::UNIFORM_VERTEX;
-	rcinit.m_vertexBuffers[0].m_buffer = verts;
-	rcinit.m_indexBuffer.m_buffer = indices;
-	rcinit.m_indexSize = 2;
-
-	ResourceGroupPtr rc0 = gr.newInstance<ResourceGroup>(rcinit);
-
-	rcinit = {};
-	rcinit.m_textures[0].m_texture = col0;
-	rcinit.m_textures[1].m_texture = col1;
-	ResourceGroupPtr rc1 = gr.newInstance<ResourceGroup>(rcinit);
-
 	//
-	// Create pplines
+	// Create progs
 	//
-	ShaderPtr vert = gr.newInstance<Shader>(ShaderType::VERTEX, VERT_MRT_SRC);
-	ShaderPtr frag = gr.newInstance<Shader>(ShaderType::FRAGMENT, FRAG_MRT_SRC);
-
-	PipelineInitInfo pinit;
-	pinit.m_shaders[ShaderType::VERTEX] = vert;
-	pinit.m_shaders[ShaderType::FRAGMENT] = frag;
-	pinit.m_color.m_attachmentCount = 2;
-	pinit.m_color.m_attachments[0].m_format = COL_FORMAT;
-	pinit.m_color.m_attachments[1].m_format = COL_FORMAT;
-	pinit.m_depthStencil.m_depthWriteEnabled = true;
-	pinit.m_depthStencil.m_format = DS_FORMAT;
-
-	pinit.m_vertex.m_attributeCount = 1;
-	pinit.m_vertex.m_attributes[0].m_format = PixelFormat(ComponentFormat::R32G32B32, TransformFormat::FLOAT);
-
-	pinit.m_vertex.m_bindingCount = 1;
-	pinit.m_vertex.m_bindings[0].m_stride = sizeof(Vec3);
-
-	PipelinePtr ppline = gr.newInstance<Pipeline>(pinit);
-
-	PipelinePtr pplineResolve = createSimplePpline(VERT_QUAD_SRC, FRAG_MRT2_SRC, gr);
+	ShaderProgramPtr prog = createProgram(VERT_MRT_SRC, FRAG_MRT_SRC, gr);
+	ShaderProgramPtr resolveProg = createProgram(VERT_QUAD_SRC, FRAG_MRT2_SRC, gr);
 
 	//
 	// Draw
@@ -991,6 +904,7 @@ static void drawOffscreen(GrManager& gr, Bool useSecondLevel)
 		gr.beginFrame();
 
 		CommandBufferInitInfo cinit;
+		cinit.m_flags = CommandBufferFlag::GRAPHICS_WORK;
 		CommandBufferPtr cmdb = gr.newInstance<CommandBuffer>(cinit);
 
 		cmdb->setPolygonOffset(0.0, 0.0);
@@ -1007,18 +921,16 @@ static void drawOffscreen(GrManager& gr, Bool useSecondLevel)
 
 		if(!useSecondLevel)
 		{
-			drawOffscreenDrawcalls(gr, ppline, rc0, cmdb, TEX_SIZE);
+			drawOffscreenDrawcalls(gr, prog, cmdb, TEX_SIZE, indices, verts);
 		}
 		else
 		{
 			CommandBufferInitInfo cinit;
-			cinit.m_flags = CommandBufferFlag::SECOND_LEVEL;
+			cinit.m_flags = CommandBufferFlag::SECOND_LEVEL | CommandBufferFlag::GRAPHICS_WORK;
 			cinit.m_framebuffer = fb;
 			CommandBufferPtr cmdb2 = gr.newInstance<CommandBuffer>(cinit);
 
-			cmdb2->setPolygonOffset(0.0, 0.0);
-
-			drawOffscreenDrawcalls(gr, ppline, rc0, cmdb2, TEX_SIZE);
+			drawOffscreenDrawcalls(gr, prog, cmdb2, TEX_SIZE, indices, verts);
 
 			cmdb->pushSecondLevelCommandBuffer(cmdb2);
 		}
@@ -1040,10 +952,11 @@ static void drawOffscreen(GrManager& gr, Bool useSecondLevel)
 
 		// Draw quad
 		cmdb->beginRenderPass(dfb);
-		cmdb->bindPipeline(pplineResolve);
+		cmdb->bindShaderProgram(resolveProg);
 		cmdb->setViewport(0, 0, WIDTH, HEIGHT);
-		cmdb->bindResourceGroup(rc1, 0, nullptr);
-		cmdb->drawArrays(6);
+		cmdb->bindTexture(0, 0, col0);
+		cmdb->bindTexture(0, 1, col1);
+		cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 6);
 		cmdb->endRenderPass();
 
 		cmdb->flush();
@@ -1092,31 +1005,12 @@ ANKI_TEST(Gr, ImageLoadStore)
 
 	TexturePtr tex = gr->newInstance<Texture>(init);
 
-	// Ppline
-	PipelinePtr ppline = createSimplePpline(VERT_QUAD_SRC, FRAG_SIMPLE_TEX_SRC, *gr);
+	// Prog
+	ShaderProgramPtr prog = createProgram(VERT_QUAD_SRC, FRAG_SIMPLE_TEX_SRC, *gr);
 
-	// Create shader & compute ppline
+	// Create shader & compute prog
 	ShaderPtr shader = gr->newInstance<Shader>(ShaderType::COMPUTE, COMP_WRITE_IMAGE_SRC);
-
-	PipelineInitInfo ppinit;
-	ppinit.m_shaders[ShaderType::COMPUTE] = shader;
-	PipelinePtr compPpline = gr->newInstance<Pipeline>(ppinit);
-
-	// RC group
-	ResourceGroupInitInfo rcinit;
-	rcinit.m_textures[0].m_texture = tex;
-	ResourceGroupPtr rc0 = gr->newInstance<ResourceGroup>(rcinit);
-
-	rcinit = ResourceGroupInitInfo();
-	rcinit.m_images[0].m_texture = tex;
-	rcinit.m_images[0].m_usage = TextureUsageBit::IMAGE_COMPUTE_WRITE;
-	rcinit.m_images[0].m_level = 1;
-	ResourceGroupPtr rc1 = gr->newInstance<ResourceGroup>(rcinit);
-
-	rcinit = ResourceGroupInitInfo();
-	rcinit.m_storageBuffers[0].m_uploadedMemory = true;
-	rcinit.m_storageBuffers[0].m_usage = BufferUsageBit::STORAGE_COMPUTE_READ;
-	ResourceGroupPtr rc2 = gr->newInstance<ResourceGroup>(rcinit);
+	ShaderProgramPtr compProg = gr->newInstance<ShaderProgram>(shader);
 
 	// FB
 	FramebufferPtr dfb = createDefaultFb(*gr);
@@ -1153,19 +1047,20 @@ ANKI_TEST(Gr, ImageLoadStore)
 		gr->beginFrame();
 
 		CommandBufferInitInfo cinit;
+		cinit.m_flags = CommandBufferFlag::GRAPHICS_WORK | CommandBufferFlag::COMPUTE_WORK;
 		CommandBufferPtr cmdb = gr->newInstance<CommandBuffer>(cinit);
 
 		// Write image
-		TransientMemoryInfo trans;
-		Vec4* col = static_cast<Vec4*>(
-			gr->allocateFrameTransientMemory(sizeof(*col), BufferUsageBit::STORAGE_ALL, trans.m_storageBuffers[0]));
+		TransientMemoryToken token;
+		Vec4* col =
+			static_cast<Vec4*>(gr->allocateFrameTransientMemory(sizeof(*col), BufferUsageBit::STORAGE_ALL, token));
 		*col = Vec4(iterations / F32(ITERATION_COUNT));
 
 		cmdb->setTextureSurfaceBarrier(
 			tex, TextureUsageBit::NONE, TextureUsageBit::IMAGE_COMPUTE_WRITE, TextureSurfaceInfo(1, 0, 0, 0));
-		cmdb->bindPipeline(compPpline);
-		cmdb->bindResourceGroup(rc1, 0, nullptr);
-		cmdb->bindResourceGroup(rc2, 1, &trans);
+		cmdb->bindShaderProgram(compProg);
+		cmdb->bindImage(0, 0, tex, 1);
+		cmdb->bindStorageBuffer(1, 0, token);
 		cmdb->dispatchCompute(WIDTH / 2, HEIGHT / 2, 1);
 		cmdb->setTextureSurfaceBarrier(tex,
 			TextureUsageBit::IMAGE_COMPUTE_WRITE,
@@ -1173,13 +1068,12 @@ ANKI_TEST(Gr, ImageLoadStore)
 			TextureSurfaceInfo(1, 0, 0, 0));
 
 		// Present image
-		cmdb->setPolygonOffset(0.0, 0.0);
 		cmdb->setViewport(0, 0, WIDTH, HEIGHT);
 
-		cmdb->bindPipeline(ppline);
+		cmdb->bindShaderProgram(prog);
 		cmdb->beginRenderPass(dfb);
-		cmdb->bindResourceGroup(rc0, 0, nullptr);
-		cmdb->drawArrays(6);
+		cmdb->bindTexture(0, 0, tex);
+		cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 6);
 		cmdb->endRenderPass();
 
 		cmdb->flush();
@@ -1282,14 +1176,7 @@ ANKI_TEST(Gr, 3DTextures)
 	//
 	// Rest
 	//
-	PipelinePtr ppline = createSimplePpline(VERT_QUAD_SRC, FRAG_TEX3D_SRC, *gr);
-
-	ResourceGroupInitInfo rcinit;
-	rcinit.m_uniformBuffers[0].m_uploadedMemory = true;
-	rcinit.m_uniformBuffers[0].m_usage = BufferUsageBit::UNIFORM_FRAGMENT;
-	rcinit.m_textures[0].m_texture = a;
-	rcinit.m_textures[0].m_usage = TextureUsageBit::SAMPLED_FRAGMENT;
-	ResourceGroupPtr rc = gr->newInstance<ResourceGroup>(rcinit);
+	ShaderProgramPtr prog = createProgram(VERT_QUAD_SRC, FRAG_TEX3D_SRC, *gr);
 
 	FramebufferPtr dfb = createDefaultFb(*gr);
 
@@ -1312,23 +1199,24 @@ ANKI_TEST(Gr, 3DTextures)
 		gr->beginFrame();
 
 		CommandBufferInitInfo cinit;
+		cinit.m_flags = CommandBufferFlag::GRAPHICS_WORK;
 		CommandBufferPtr cmdb = gr->newInstance<CommandBuffer>(cinit);
 
-		cmdb->setPolygonOffset(0.0, 0.0);
 		cmdb->setViewport(0, 0, WIDTH, HEIGHT);
 		cmdb->beginRenderPass(dfb);
 
-		cmdb->bindPipeline(ppline);
+		cmdb->bindShaderProgram(prog);
 
-		TransientMemoryInfo transientInfo;
-		Vec4* uv = static_cast<Vec4*>(gr->allocateFrameTransientMemory(
-			sizeof(Vec4), BufferUsageBit::UNIFORM_ALL, transientInfo.m_uniformBuffers[0]));
+		TransientMemoryToken token;
+		Vec4* uv =
+			static_cast<Vec4*>(gr->allocateFrameTransientMemory(sizeof(Vec4), BufferUsageBit::UNIFORM_ALL, token));
 
 		U idx = (F32(ITERATION_COUNT - iterations - 1) / ITERATION_COUNT) * TEX_COORDS_LOD.getSize();
 		*uv = TEX_COORDS_LOD[idx];
 
-		cmdb->bindResourceGroup(rc, 0, &transientInfo);
-		cmdb->drawArrays(6);
+		cmdb->bindUniformBuffer(0, 0, token);
+		cmdb->bindTexture(0, 0, a);
+		cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 6);
 
 		cmdb->endRenderPass();
 

+ 17 - 1
tools/format_source.sh

@@ -1 +1,17 @@
-find ./src ./tests ./sandbox ./tools ./shaders ./samples -name '*.h' -o -name '*.hpp' -o -name '*.c' -o -name '*.cpp' -o -name '*.glsl' | xargs -I % ./thirdparty/bin/clang-format -sort-includes=false -i %
+#!/bin/bash
+
+files=(`find ./src ./tests ./sandbox ./tools ./shaders ./samples -name '*.h' -o -name '*.hpp' -o -name '*.c' -o -name '*.cpp' -o -name '*.glsl'`)
+
+filecount=${#files[@]}
+
+count=0
+for f in ${files[@]}
+do
+	echo -ne Formatting ${count}/${filecount}\\r
+	./thirdparty/bin/clang-format -sort-includes=false -i ${f}
+	count=$((${count}+1))
+done
+
+echo Done! Formatted ${filecount} files
+
+

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio