Browse Source

Adding proper support for dynamic GPU memory

Panagiotis Christopoulos Charitos 10 years ago
parent
commit
0e19819d6a

+ 33 - 32
include/anki/Config.h.cmake

@@ -1,5 +1,9 @@
-#ifndef ANKI_CONFIG_H
-#define ANKI_CONFIG_H
+// Copyright (C) 2009-2015, Panagiotis Christopoulos Charitos.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
 
 /// @addtogroup config
 /// @{
@@ -104,34 +108,6 @@
 // Enable performance counters
 #define ANKI_ENABLE_COUNTERS ${_ANKI_ENABLE_COUNTERS}
 
-//==============================================================================
-// Engine config                                                               =
-//==============================================================================
-
-// General config
-#define ANKI_SAFE_ALIGNMENT 16
-
-// Renderer and rendering related config options
-#define ANKI_RENDERER_MAX_TILES_X 64
-#define ANKI_RENDERER_MAX_TILES_Y 64
-
-#define ANKI_RENDERER_USE_MATERIAL_UBOS 0
-
-/// @{
-/// Used to optimize the initial vectors of VisibilityTestResults
-#define ANKI_FRUSTUMABLE_AVERAGE_VISIBLE_RENDERABLES_COUNT 16
-#define ANKI_FRUSTUMABLE_AVERAGE_VISIBLE_LIGHTS_COUNT 8
-/// @}
-
-/// If true then we can place spatials in a thread-safe way
-#define ANKI_CFG_OCTREE_THREAD_SAFE 1
-
-// GL
-#define ANKI_GL_MAX_MIPMAPS 32
-#define ANKI_GL_MAX_TEXTURE_LAYERS 32
-#define ANKI_GL_MAX_SUB_DRAWCALLS 64
-#define ANKI_GL_MAX_INSTANCES 64
-
 //==============================================================================
 // Other                                                                       =
 //==============================================================================
@@ -153,7 +129,6 @@
 #	define ANKI_USE_RESULT
 #	define ANKI_FORCE_INLINE
 #endif
-/// @}
 
 #ifdef ANKI_BUILD
 #	define anki_internal public
@@ -161,4 +136,30 @@
 #	define anki_internal protected
 #endif
 
-#endif
+//==============================================================================
+// Engine config                                                               =
+//==============================================================================
+
+// General config
+#define ANKI_SAFE_ALIGNMENT 16
+
+// GL
+#define ANKI_GL_MAX_SUB_DRAWCALLS 64
+
+namespace anki {
+
+/// Max size of the dynamic uniforms ring buffer. You will get a warning it's
+/// not enough
+const unsigned GR_DYNAMIC_UNIFORMS_SIZE = 1024 * 1024 * 16;
+
+/// Max size of the dynamic storage ring buffer. You will get a warning it's
+/// not enough
+const unsigned GR_DYNAMIC_STORAGE_SIZE = 1024 * 1024 * 16;
+
+/// Max size of the dynamic vertex ring buffer. You will get a warning it's
+/// not enough
+const unsigned GR_DYNAMIC_VERTEX_SIZE = 1024 * 1024 * 2;
+
+} // end namespace anki
+
+/// @}

+ 2 - 1
include/anki/core/Counters.h

@@ -35,7 +35,8 @@ enum class Counter
 	GL_VERTICES_COUNT,
 	GL_QUEUES_SIZE,
 	GL_CLIENT_BUFFERS_SIZE,
-	GR_UNIFORM_SIZE,
+	GR_DYNAMIC_UNIFORMS_SIZE,
+	GR_DYNAMIC_STORAGE_SIZE,
 
 	COUNT
 };

+ 13 - 8
include/anki/gr/CommandBuffer.h

@@ -5,7 +5,8 @@
 
 #pragma once
 
-#include "anki/gr/GrObject.h"
+#include <anki/gr/GrObject.h>
+#include <anki/util/Functions.h>
 
 namespace anki {
 
@@ -114,7 +115,8 @@ public:
 	void bindFramebuffer(FramebufferPtr fb);
 
 	/// Bind resources.
-	void bindResourceGroup(ResourceGroupPtr rc, U slot);
+	void bindResourceGroup(ResourceGroupPtr rc, U slot,
+		const DynamicBufferInfo* dynInfo);
 	/// @}
 
 	/// @name Jobs
@@ -163,13 +165,15 @@ public:
 		data = static_cast<Type*>(vdata);
 	}
 
-	/// Update dynamic uniforms.
+	/// Allocate memory for dynamic buffers.
 	template<typename Type>
-	void updateDynamicUniforms(U32 size, Type*& data)
+	Type* allocateDynamicMemory(U32 count, BufferUsage usage,
+		DynamicBufferToken& token)
 	{
-		void* vdata = nullptr;
-		updateDynamicUniformsInternal(size, vdata);
-		data = static_cast<Type*>(vdata);
+		void* ptr = allocateDynamicMemoryInternal(count * sizeof(Type),
+			usage, token);
+		ANKI_ASSERT(isAligned(alignof(Type), static_cast<U8*>(ptr)));
+		return static_cast<Type*>(ptr);
 	}
 	/// @}
 
@@ -197,7 +201,8 @@ private:
 	void writeBufferInternal(BufferPtr buff, PtrSize offset, PtrSize range,
 		void*& data);
 
-	void updateDynamicUniformsInternal(U32 size, void*& data);
+	void* allocateDynamicMemoryInternal(U32 size, BufferUsage usage,
+		DynamicBufferToken& token);
 };
 /// @}
 

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

@@ -20,6 +20,9 @@ class GrManagerInitializer;
 class PipelineInitializer;
 class FramebufferInitializer;
 
+class DynamicBufferToken;
+class DynamicBufferInfo;
+
 #define ANKI_GR_CLASS(x_) \
 	class x_##Impl; \
 	class x_; \
@@ -49,7 +52,7 @@ const U MAX_COLOR_ATTACHMENTS = 4;
 const U MAX_MIPMAPS = 16;
 const U MAX_TEXTURE_LAYERS = 32;
 const U MAX_TEXTURE_BINDINGS = 8;
-const U MAX_UNIFORM_BUFFER_BINDINGS = 8;
+const U MAX_UNIFORM_BUFFER_BINDINGS = 1;
 const U MAX_STORAGE_BUFFER_BINDINGS = 8;
 const U MAX_FRAMES_IN_FLIGHT = 3;
 const U MAX_RESOURCE_GROUPS = 2;

+ 14 - 0
include/anki/gr/Enums.h

@@ -282,6 +282,20 @@ enum class BufferUsageBit: U8
 };
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(BufferUsageBit, inline)
 
+/// Buffer usage modes.
+enum class BufferUsage: U8
+{
+	UNIFORM,
+	STORAGE,
+	INDEX,
+	VERTEX,
+	INDIRECT,
+
+	COUNT,
+	FIRST = UNIFORM
+};
+ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(BufferUsage, inline)
+
 /// Buffer access from client modes.
 enum class BufferAccessBit: U8
 {

+ 29 - 5
include/anki/gr/ResourceGroup.h

@@ -5,10 +5,10 @@
 
 #pragma once
 
-#include "anki/gr/GrObject.h"
-#include "anki/gr/Texture.h"
-#include "anki/gr/Sampler.h"
-#include "anki/gr/Buffer.h"
+#include <anki/gr/GrObject.h>
+#include <anki/gr/Texture.h>
+#include <anki/gr/Sampler.h>
+#include <anki/gr/Buffer.h>
 
 namespace anki {
 
@@ -23,13 +23,14 @@ public:
 	SamplerPtr m_sampler; ///< Use it to override texture's sampler.
 };
 
-/// Buffer binding point.
+/// Buffer binding info.
 class BufferBinding
 {
 public:
 	BufferPtr m_buffer;
 	PtrSize m_offset = 0;
 	PtrSize m_range = 0;
+	Bool m_dynamic = false;
 };
 
 /// Resource group initializer.
@@ -44,6 +45,29 @@ public:
 	I8 m_indexSize = -1; ///< Index size in bytes. 2 or 4
 };
 
+/// Token that gets returned when requesting for memory to write to a dynamic
+/// buffer.
+class DynamicBufferToken
+{
+anki_internal:
+	U32 m_offset = 0;
+	U32 m_range = 0;
+
+	void invalidate()
+	{
+		m_offset = m_range = MAX_U32;
+	}
+};
+
+/// Struct to help update the offset of the dynamic buffers.
+class DynamicBufferInfo
+{
+public:
+	Array<DynamicBufferToken, MAX_UNIFORM_BUFFER_BINDINGS> m_uniformBuffers;
+	Array<DynamicBufferToken, MAX_STORAGE_BUFFER_BINDINGS> m_storageBuffers;
+	Array<DynamicBufferToken, MAX_VERTEX_ATTRIBUTES> m_vertexBuffers;
+};
+
 /// Resource group.
 class ResourceGroup: public GrObject
 {

+ 57 - 10
include/anki/gr/gl/GlState.h

@@ -26,13 +26,13 @@ enum class GpuVendor: U8
 class GlState
 {
 public:
-	static const U MAX_UBO_SIZE = 16384;
+	// Spec limits
+	static const U MAX_UNIFORM_BLOCK_SIZE = 16384;
+	static const U MAX_STORAGE_BLOCK_SIZE = 2 << 27;
 
 	I32 m_version = -1; ///< Minor major GL version. Something like 430
 	GpuVendor m_gpu = GpuVendor::UNKNOWN;
 	Bool8 m_registerMessages = false;
-	U32 m_uniBuffOffsetAlignment = 0;
-	U32 m_ssBuffOffsetAlignment = 0;
 
 	GLuint m_defaultVao;
 
@@ -72,13 +72,23 @@ public:
 	Bool m_depthWriteMask = true;
 	/// @}
 
-	/// Global UBO ring buffer
+	/// Ring buffers
 	/// @{
-	U32 m_globalUboSize = MAX_UBO_SIZE * 512; ///< 32MB
-	DArray<GLuint> m_globalUbos; ///< Multiple cause of the spec's UBO max size.
-	DArray<U8*> m_globalUboAddresses;
-	Atomic<U32> m_globalUboCurrentOffset = {0};
-	Atomic<U32> m_globalUboBytesUsaged = {0}; ///< Per frame
+
+	// Ring buffer for dynamic buffers.
+	class DynamicBuffer
+	{
+	public:
+		GLuint m_name;
+		U8* m_address;
+		PtrSize m_size; ///< This is aligned compared to GLOBAL_XXX_SIZE
+		U32 m_alignment;
+		U32 m_maxAllocationSize; ///< For debugging.
+		Atomic<PtrSize> m_currentOffset = {0};
+		Atomic<PtrSize> m_bytesUsed = {0}; ///< Per frame. Mainly for debug
+	};
+
+	Array<DynamicBuffer, U(BufferUsage::COUNT)> m_dynamicBuffers;
 	/// @}
 
 	GlState(GrManager* manager)
@@ -91,11 +101,48 @@ public:
 	/// Call this from the rendering thread.
 	void destroy();
 
+	/// Allocate memory for a dynamic buffer.
+	void* allocateDynamicMemory(PtrSize size, BufferUsage usage);
+
+	void checkDynamicMemoryConsumption();
+
 private:
 	GrManager* m_manager;
 
-	void initGlobalUbo();
+	void initDynamicBuffer(GLenum target, U32 aligment, PtrSize size,
+		U32 maxAllocationSize, BufferUsage usage);
 };
+
+//==============================================================================
+inline void* GlState::allocateDynamicMemory(PtrSize size, BufferUsage usage)
+{
+	ANKI_ASSERT(size > 0);
+
+	DynamicBuffer& buff = m_dynamicBuffers[usage];
+	ANKI_ASSERT(buff.m_name != 0);
+
+	// Align size
+	size = getAlignedRoundUp(buff.m_alignment, size);
+	ANKI_ASSERT(size <= buff.m_maxAllocationSize && "Too high!");
+
+	// Allocate
+	PtrSize offset = 0;
+
+	// Run in loop in case the begining of the range falls inside but the end
+	// outside the buffer
+	do
+	{
+		offset = buff.m_currentOffset.fetchAdd(size);
+		offset = offset % buff.m_size;
+	} while((offset + size) > buff.m_size);
+
+	ANKI_ASSERT(isAligned(buff.m_alignment, buff.m_address + offset));
+	ANKI_ASSERT((offset + size) <= buff.m_size);
+
+	buff.m_bytesUsed.fetchAdd(size);
+
+	return static_cast<void*>(buff.m_address + offset);
+}
 /// @}
 
 } // end namespace anki

+ 7 - 6
include/anki/gr/gl/ResourceGroupImpl.h

@@ -5,9 +5,9 @@
 
 #pragma once
 
-#include "anki/gr/gl/GlObject.h"
-#include "anki/gr/ResourceGroup.h"
-#include "anki/util/DArray.h"
+#include <anki/gr/gl/GlObject.h>
+#include <anki/gr/ResourceGroup.h>
+#include <anki/util/DArray.h>
 
 namespace anki {
 
@@ -30,13 +30,13 @@ public:
 	void create(const ResourceGroupInitializer& init);
 
 	/// Set state.
-	void bind(U slot, GlState& state);
+	void bind(U slot, const DynamicBufferInfo& dynInfo, GlState& state);
 
 private:
 	class InternalBufferBinding
 	{
 	public:
-		GLuint m_name = 0;
+		GLuint m_name = 0; ///< If it's MAX_U32 then it's dynamic
 		U32 m_offset = 0;
 		U32 m_range = 0;
 	};
@@ -54,6 +54,7 @@ private:
 
 	Array<GLuint, MAX_VERTEX_ATTRIBUTES> m_vertBuffNames;
 	Array<GLintptr, MAX_VERTEX_ATTRIBUTES> m_vertBuffOffsets;
+	Bool8 m_hasDynamicVertexBuff = false;
 	U8 m_vertBindingsCount = 0;
 
 	GLuint m_indexBuffName = 0;
@@ -65,7 +66,7 @@ private:
 
 	template<typename InBindings, typename OutBindings>
 	void initBuffers(const InBindings& in, OutBindings& out, U8& count,
-		U& resourcesCount);
+		U& resourcesCount, U& dynCount);
 
 	void initResourceReferences(const ResourceGroupInitializer& init, U count);
 };

+ 1 - 1
include/anki/renderer/Fs.h

@@ -28,7 +28,7 @@ anki_internal:
 private:
 	FramebufferPtr m_fb;
 
-	Array<ResourceGroupPtr, MAX_FRAMES_IN_FLIGHT> m_globalResources;
+	ResourceGroupPtr m_globalResources;
 };
 /// @}
 

+ 2 - 3
include/anki/renderer/Ir.h

@@ -28,7 +28,7 @@ anki_internal:
 
 	ANKI_USE_RESULT Error init(const ConfigSet& initializer);
 
-	ANKI_USE_RESULT Error run();
+	ANKI_USE_RESULT Error run(CommandBufferPtr cmdb);
 
 	TexturePtr getCubemapArray() const
 	{
@@ -40,8 +40,7 @@ private:
 	TexturePtr m_cubemapArr;
 	U16 m_cubemapArrSize = 0;
 	U16 m_fbSize = 0;
-	U16 m_probesBuffSize = 0;
-	Array<BufferPtr, MAX_FRAMES_IN_FLIGHT> m_probesBuff;
+	DynamicBufferToken m_probesToken;
 
 	ANKI_USE_RESULT Error renderReflection(SceneNode& node,
 		ShaderReflectionProbe& shaderProb);

+ 16 - 31
include/anki/renderer/Is.h

@@ -72,29 +72,29 @@ anki_internal:
 		return m_sm;
 	}
 
-	BufferPtr getCommonVarsBuffer(U idx) const
+	DynamicBufferToken getCommonVarsToken() const
 	{
-		return m_commonVarsBuffs[idx];
+		return m_commonVarsToken;
 	}
 
-	BufferPtr getPointLightsBuffer(U idx) const
+	DynamicBufferToken getPointLightsToken() const
 	{
-		return m_pLightsBuffs[idx];
+		return m_pLightsToken;
 	}
 
-	BufferPtr getSpotLightsBuffer(U idx) const
+	DynamicBufferToken getSpotLightsToken() const
 	{
-		return m_sLightsBuffs[idx];
+		return m_sLightsToken;
 	}
 
-	BufferPtr getClusterBuffer(U idx) const
+	DynamicBufferToken getClustersToken() const
 	{
-		return m_clusterBuffers[idx];
+		return m_clustersToken;
 	}
 
-	BufferPtr getLightIndicesBuffer(U idx) const
+	DynamicBufferToken getLightIndicesToken() const
 	{
-		return m_lightIdsBuffers[idx];
+		return m_lightIdsToken;
 	}
 
 private:
@@ -106,28 +106,13 @@ private:
 	/// The IS FBO
 	FramebufferPtr m_fb;
 
-	/// @name GPU buffers
-	/// @{
-
-	/// Contains some variables.
-	Array<BufferPtr, MAX_FRAMES_IN_FLIGHT> m_commonVarsBuffs;
-
-	/// Contains all the point lights
-	Array<BufferPtr, MAX_FRAMES_IN_FLIGHT> m_pLightsBuffs;
-	U32 m_pLightsBuffSize = 0;
-
-	/// Contains all the spot lights
-	Array<BufferPtr, MAX_FRAMES_IN_FLIGHT> m_sLightsBuffs;
-	U32 m_sLightsBuffSize = 0;
-
-	/// Contains the cluster info
-	Array<BufferPtr, MAX_FRAMES_IN_FLIGHT> m_clusterBuffers;
-
-	/// Contains light indices.
-	Array<BufferPtr, MAX_FRAMES_IN_FLIGHT> m_lightIdsBuffers;
-	/// @}
+	DynamicBufferToken m_commonVarsToken;
+	DynamicBufferToken m_pLightsToken;
+	DynamicBufferToken m_sLightsToken;
+	DynamicBufferToken m_clustersToken;
+	DynamicBufferToken m_lightIdsToken;
 
-	Array<ResourceGroupPtr, MAX_FRAMES_IN_FLIGHT> m_rcGroups;
+	ResourceGroupPtr m_rcGroup;
 
 	// Light shaders
 	ShaderResourcePtr m_lightVert;

+ 1 - 3
include/anki/renderer/Lf.h

@@ -34,12 +34,10 @@ anki_internal:
 
 private:
 	// Occlusion query
-	Array<BufferPtr, MAX_FRAMES_IN_FLIGHT> m_positionsVertBuff;
-	U32 m_positionsVertBuffSize;
 	ShaderResourcePtr m_occlusionVert;
 	ShaderResourcePtr m_occlusionFrag;
 	PipelinePtr m_occlusionPpline;
-	Array<ResourceGroupPtr, MAX_FRAMES_IN_FLIGHT> m_occlusionRcGroups;
+	ResourceGroupPtr m_occlusionRcGroup;
 
 	// Sprite billboards
 	ShaderResourcePtr m_realVert;

+ 1 - 0
include/anki/scene/RenderComponent.h

@@ -112,6 +112,7 @@ public:
 	const U8* m_subMeshIndicesArray; ///< @note indices != drawing indices
 	U32 m_subMeshIndicesCount;
 	CommandBufferPtr m_cmdb; ///< A command buffer to append to.
+	DynamicBufferInfo* m_dynamicBufferInfo ANKI_DBG_NULLIFY_PTR;
 };
 
 /// RenderComponent interface. Implemented by renderable scene nodes

+ 1 - 1
shaders/Common.glsl

@@ -29,8 +29,8 @@ const float PI = 3.14159265358979323846;
 #define textureRt(tex_, texc_) textureLod(tex_, texc_, 0.0)
 
 // Binding
+#define UBO_BINDING(slot_, binding_) binding = slot_ * 1 + binding_
 #define SS_BINDING(slot_, binding_) binding = slot_ * 8 + binding_
-#define UBO_BINDING(slot_, binding_) binding = slot_ * 8 + binding_
 #define TEX_BINDING(slot_, binding_) binding = slot_ * 8 + binding_
 
 // Common locations

+ 2 - 1
src/core/Counters.cpp

@@ -48,7 +48,8 @@ static const Array<CounterInfo, (U)Counter::COUNT> cinfo = {{
 	{"GL_VERTICES_COUNT", CF_PER_FRAME | CF_PER_RUN | CF_U64},
 	{"GL_QUEUES_SIZE", CF_PER_FRAME | CF_PER_RUN | CF_U64},
 	{"GL_CLIENT_BUFFERS_SIZE", CF_PER_FRAME | CF_PER_RUN | CF_U64},
-	{"GR_UNIFORM_SIZE", CF_PER_FRAME | CF_PER_RUN | CF_U64}
+	{"GR_DYNAMIC_UNIFORMS_SIZE", CF_PER_FRAME | CF_PER_RUN | CF_U64},
+	{"GR_DYNAMIC_STORAGE_SIZE", CF_PER_FRAME | CF_PER_RUN | CF_U64}
 }};
 
 #define MAX_NAME "25"

+ 42 - 79
src/gr/gl/CommandBuffer.cpp

@@ -3,24 +3,24 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#include "anki/gr/CommandBuffer.h"
-#include "anki/gr/gl/CommandBufferImpl.h"
-#include "anki/gr/GrManager.h"
-#include "anki/gr/gl/GrManagerImpl.h"
-#include "anki/gr/gl/RenderingThread.h"
-#include "anki/gr/Pipeline.h"
-#include "anki/gr/gl/PipelineImpl.h"
-#include "anki/gr/Framebuffer.h"
-#include "anki/gr/gl/FramebufferImpl.h"
-#include "anki/gr/ResourceGroup.h"
-#include "anki/gr/gl/ResourceGroupImpl.h"
-#include "anki/gr/OcclusionQuery.h"
-#include "anki/gr/gl/OcclusionQueryImpl.h"
-#include "anki/gr/Texture.h"
-#include "anki/gr/gl/TextureImpl.h"
-#include "anki/gr/Buffer.h"
-#include "anki/gr/gl/BufferImpl.h"
-#include "anki/core/Counters.h"
+#include <anki/gr/CommandBuffer.h>
+#include <anki/gr/gl/CommandBufferImpl.h>
+#include <anki/gr/GrManager.h>
+#include <anki/gr/gl/GrManagerImpl.h>
+#include <anki/gr/gl/RenderingThread.h>
+#include <anki/gr/Pipeline.h>
+#include <anki/gr/gl/PipelineImpl.h>
+#include <anki/gr/Framebuffer.h>
+#include <anki/gr/gl/FramebufferImpl.h>
+#include <anki/gr/ResourceGroup.h>
+#include <anki/gr/gl/ResourceGroupImpl.h>
+#include <anki/gr/OcclusionQuery.h>
+#include <anki/gr/gl/OcclusionQueryImpl.h>
+#include <anki/gr/Texture.h>
+#include <anki/gr/gl/TextureImpl.h>
+#include <anki/gr/Buffer.h>
+#include <anki/gr/gl/BufferImpl.h>
+#include <anki/core/Counters.h>
 
 namespace anki {
 
@@ -144,23 +144,31 @@ class BindResourcesCommand final: public GlCommand
 public:
 	ResourceGroupPtr m_rc;
 	U8 m_slot;
+	DynamicBufferInfo m_dynInfo;
 
-	BindResourcesCommand(ResourceGroupPtr rc, U8 slot)
+	BindResourcesCommand(ResourceGroupPtr rc, U8 slot,
+		const DynamicBufferInfo* dynInfo)
 		: m_rc(rc)
 		, m_slot(slot)
-	{}
+	{
+		if(dynInfo)
+		{
+			m_dynInfo = *dynInfo;
+		}
+	}
 
 	Error operator()(GlState& state)
 	{
-		m_rc->getImplementation().bind(m_slot, state);
+		m_rc->getImplementation().bind(m_slot, m_dynInfo, state);
 		return ErrorCode::NONE;
 	}
 };
 
-void CommandBuffer::bindResourceGroup(ResourceGroupPtr rc, U slot)
+void CommandBuffer::bindResourceGroup(ResourceGroupPtr rc, U slot,
+	const DynamicBufferInfo* dynInfo)
 {
 	ANKI_ASSERT(rc.isCreated());
-	m_impl->pushBackNewCommand<BindResourcesCommand>(rc, slot);
+	m_impl->pushBackNewCommand<BindResourcesCommand>(rc, slot, dynInfo);
 }
 
 //==============================================================================
@@ -306,69 +314,24 @@ void CommandBuffer::dispatchCompute(
 }
 
 //==============================================================================
-class UpdateUniformsCommand final: public GlCommand
+void* CommandBuffer::allocateDynamicMemoryInternal(U32 size, BufferUsage usage,
+	DynamicBufferToken& token)
 {
-public:
-	GLuint m_uboName;
-	U32 m_offset;
-	U16 m_range;
-
-	UpdateUniformsCommand(GLuint ubo, U32 offset, U16 range)
-		: m_uboName(ubo)
-		, m_offset(offset)
-		, m_range(range)
-	{}
-
-	Error operator()(GlState&)
-	{
-		const U binding = 0;
-		glBindBufferRange(
-			GL_UNIFORM_BUFFER, binding, m_uboName, m_offset, m_range);
-
-		return ErrorCode::NONE;
-	}
-};
-
-void CommandBuffer::updateDynamicUniformsInternal(U32 originalSize, void*& data)
-{
-	ANKI_ASSERT(originalSize > 0);
-	ANKI_ASSERT(originalSize <= 1024 * 8 && "Too high?");
-
 	// Will be used in a thread safe way
 	GlState& state =
 		getManager().getImplementation().getRenderingThread().getState();
 
-	const U uboSize = state.m_globalUboSize;
-	const U subUboSize = GlState::MAX_UBO_SIZE;
-
-	// Get offset in the contiguous buffer
-	U size = getAlignedRoundUp(state.m_uniBuffOffsetAlignment, originalSize);
-	state.m_globalUboBytesUsaged.fetchAdd(size);
-	U offset = state.m_globalUboCurrentOffset.fetchAdd(size);
-	offset = offset % uboSize;
-
-	while((offset % subUboSize) + size > subUboSize)
-	{
-		// Update area will fall between UBOs, need to start over
-		offset = state.m_globalUboCurrentOffset.fetchAdd(size);
-		offset = offset % uboSize;
-	}
-
-	ANKI_ASSERT(isAligned(state.m_uniBuffOffsetAlignment, offset));
-	ANKI_ASSERT(offset + size <= uboSize);
-
-	// Get actual UBO address to write
-	U uboIdx = offset / subUboSize;
-	U subUboOffset = offset % subUboSize;
-	ANKI_ASSERT(isAligned(state.m_uniBuffOffsetAlignment, subUboOffset));
-
-	U8* addressToWrite = state.m_globalUboAddresses[uboIdx] + subUboOffset;
+	void* data = state.allocateDynamicMemory(size, usage);
+	ANKI_ASSERT(data);
 
-	data = static_cast<void*>(addressToWrite);
+	// Encode token
+	PtrSize offset =
+		static_cast<U8*>(data) - state.m_dynamicBuffers[usage].m_address;
+	ANKI_ASSERT(offset < MAX_U32 && size < MAX_U32);
+	token.m_offset = offset;
+	token.m_range = size;
 
-	// Push bind command
-	m_impl->pushBackNewCommand<UpdateUniformsCommand>(
-		state.m_globalUbos[uboIdx], subUboOffset, originalSize);
+	return data;
 }
 
 //==============================================================================

+ 65 - 37
src/gr/gl/GlState.cpp

@@ -3,10 +3,11 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#include "anki/gr/gl/GlState.h"
-#include "anki/gr/gl/BufferImpl.h"
-#include "anki/gr/GrManager.h"
-#include "anki/util/Logger.h"
+#include <anki/gr/gl/GlState.h>
+#include <anki/gr/gl/BufferImpl.h>
+#include <anki/gr/GrManager.h>
+#include <anki/util/Logger.h>
+#include <anki/core/Counters.h>
 #include <algorithm>
 #include <cstring>
 
@@ -119,15 +120,6 @@ void GlState::init()
 		}
 	}
 #endif
-
-	// Buffer offset alignment
-	GLint64 alignment;
-	glGetInteger64v(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &alignment);
-	m_uniBuffOffsetAlignment = alignment;
-
-	glGetInteger64v(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT, &alignment);
-	m_ssBuffOffsetAlignment = alignment;
-
 	// Set some GL state
 	glEnable(GL_PROGRAM_POINT_SIZE);
 	glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
@@ -144,51 +136,87 @@ void GlState::init()
 
 	// Other
 	memset(&m_vertexBindingStrides[0], 0, sizeof(m_vertexBindingStrides));
-	initGlobalUbo();
+
+	// Init ring buffers
+	GLint64 blockAlignment;
+	glGetInteger64v(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &blockAlignment);
+	initDynamicBuffer(GL_UNIFORM_BUFFER, blockAlignment,
+		GR_DYNAMIC_UNIFORMS_SIZE, MAX_UNIFORM_BLOCK_SIZE, BufferUsage::UNIFORM);
+
+	glGetInteger64v(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT, &blockAlignment);
+	initDynamicBuffer(GL_SHADER_STORAGE_BUFFER, blockAlignment,
+		GR_DYNAMIC_STORAGE_SIZE, MAX_STORAGE_BLOCK_SIZE, BufferUsage::STORAGE);
+
+	initDynamicBuffer(GL_ARRAY_BUFFER, 8, GR_DYNAMIC_VERTEX_SIZE,
+		MAX_U32, BufferUsage::VERTEX);
 }
 
 //==============================================================================
-void GlState::initGlobalUbo()
+void GlState::initDynamicBuffer(GLenum target, U32 alignment, PtrSize size,
+	U32 maxAllocationSize, BufferUsage usage)
 {
-	ANKI_ASSERT(m_globalUboSize > 0);
-	auto alloc = m_manager->getAllocator();
+	DynamicBuffer& buff = m_dynamicBuffers[usage];
 
-	m_globalUboSize = getAlignedRoundUp(MAX_UBO_SIZE, m_globalUboSize);
-	U ubosCount = m_globalUboSize / MAX_UBO_SIZE;
+	const U FLAGS = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT;
 
-	m_globalUbos.create(alloc, ubosCount);
-	m_globalUboAddresses.create(alloc, ubosCount);
+	alignRoundUp(alignment, size);
 
-	for(U i = 0; i < ubosCount; ++i)
-	{
-		const U FLAGS = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT;
+	glGenBuffers(1, &buff.m_name);
 
-		GLuint name;
-		glGenBuffers(1, &name);
+	glBindBuffer(target, buff.m_name);
+	glBufferStorage(target, size, nullptr, FLAGS);
 
-		glBindBuffer(GL_UNIFORM_BUFFER, name);
-		glBufferStorage(GL_UNIFORM_BUFFER, MAX_UBO_SIZE, nullptr, FLAGS);
+	buff.m_address = static_cast<U8*>(glMapBufferRange(target, 0, size, FLAGS));
+	buff.m_size = size;
+	buff.m_alignment = alignment;
+	buff.m_maxAllocationSize = maxAllocationSize;
+}
 
-		m_globalUbos[i] = name;
+//==============================================================================
+void GlState::checkDynamicMemoryConsumption()
+{
+	for(BufferUsage usage = BufferUsage::FIRST; usage < BufferUsage::COUNT;
+		++usage)
+	{
+		DynamicBuffer& buff = m_dynamicBuffers[usage];
 
-		m_globalUboAddresses[i] = static_cast<U8*>(
-			glMapBufferRange(GL_UNIFORM_BUFFER, 0, MAX_UBO_SIZE, FLAGS));
+		if(buff.m_name)
+		{
+			auto bytesUsed = buff.m_bytesUsed.exchange(0);
+			if(bytesUsed >= buff.m_size / MAX_FRAMES_IN_FLIGHT)
+			{
+				ANKI_LOGW("Using too much dynamic memory (type: %u). "
+					"Increase the limit", U(usage));
+			}
+
+			// Increase the counters
+			switch(usage)
+			{
+			case BufferUsage::UNIFORM:
+				ANKI_COUNTER_INC(GR_DYNAMIC_UNIFORMS_SIZE, U64(bytesUsed));
+				break;
+			case BufferUsage::STORAGE:
+				ANKI_COUNTER_INC(GR_DYNAMIC_STORAGE_SIZE, U64(bytesUsed));
+				break;
+			default:
+				break;
+			}
+		}
 	}
 }
 
 //==============================================================================
 void GlState::destroy()
 {
-	for(GLuint name : m_globalUbos)
+	for(auto& x : m_dynamicBuffers)
 	{
-		glDeleteBuffers(1, &name);
+		if(x.m_name)
+		{
+			glDeleteBuffers(1, &x.m_name);
+		}
 	}
 
 	glDeleteVertexArrays(1, &m_defaultVao);
-
-	auto alloc = m_manager->getAllocator();
-	m_globalUbos.destroy(alloc);
-	m_globalUboAddresses.destroy(alloc);
 }
 
 } // end namespace anki

+ 7 - 13
src/gr/gl/RenderingThread.cpp

@@ -3,12 +3,12 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#include "anki/gr/gl/RenderingThread.h"
-#include "anki/gr/gl/CommandBufferImpl.h"
-#include "anki/gr/GrManager.h"
-#include "anki/gr/gl/GrManagerImpl.h"
-#include "anki/util/Logger.h"
-#include "anki/core/Counters.h"
+#include <anki/gr/gl/RenderingThread.h>
+#include <anki/gr/gl/CommandBufferImpl.h>
+#include <anki/gr/GrManager.h>
+#include <anki/gr/gl/GrManagerImpl.h>
+#include <anki/util/Logger.h>
+#include <anki/core/Counters.h>
 
 namespace anki {
 
@@ -296,13 +296,7 @@ void RenderingThread::swapBuffersInternal(GlState& state)
 	}
 
 	m_frameCondVar.notifyOne();
-
-	auto bytesUsed = state.m_globalUboBytesUsaged.exchange(0);
-	ANKI_COUNTER_INC(GR_UNIFORM_SIZE, U64(bytesUsed));
-	if(bytesUsed >= state.m_globalUboSize / MAX_FRAMES_IN_FLIGHT)
-	{
-		ANKI_LOGW("Using too much uniform memory. Increase the limit");
-	}
+	state.checkDynamicMemoryConsumption();
 }
 
 //==============================================================================

+ 116 - 16
src/gr/gl/ResourceGroupImpl.cpp

@@ -20,7 +20,8 @@ namespace anki {
 //==============================================================================
 template<typename InBindings, typename OutBindings>
 void ResourceGroupImpl::initBuffers(
-	const InBindings& in, OutBindings& out, U8& count, U& resourcesCount)
+	const InBindings& in, OutBindings& out, U8& count, U& resourcesCount,
+	U& dynCount)
 {
 	count = 0;
 
@@ -30,6 +31,8 @@ void ResourceGroupImpl::initBuffers(
 
 		if(binding.m_buffer.isCreated())
 		{
+			ANKI_ASSERT(binding.m_dynamic == false);
+
 			const BufferImpl& buff = binding.m_buffer->getImplementation();
 			InternalBufferBinding& outBinding = out[count];
 
@@ -44,8 +47,15 @@ void ResourceGroupImpl::initBuffers(
 				outBinding.m_offset + outBinding.m_range <= buff.m_size);
 			ANKI_ASSERT(outBinding.m_range > 0);
 
-			++count;
 			++resourcesCount;
+			count = i + 1;
+		}
+		else if(binding.m_dynamic)
+		{
+			InternalBufferBinding& outBinding = out[count];
+			outBinding.m_name = MAX_U32;
+			++dynCount;
+			count = i + 1;
 		}
 	}
 }
@@ -54,6 +64,7 @@ void ResourceGroupImpl::initBuffers(
 void ResourceGroupImpl::create(const ResourceGroupInitializer& init)
 {
 	U resourcesCount = 0;
+	U dynCount = 0;
 
 	// Init textures & samplers
 	m_textureNamesCount = 0;
@@ -85,9 +96,11 @@ void ResourceGroupImpl::create(const ResourceGroupInitializer& init)
 		}
 	}
 
-	// Init buffers
-	initBuffers(init.m_uniformBuffers, m_ubos, m_ubosCount, resourcesCount);
-	initBuffers(init.m_storageBuffers, m_ssbos, m_ssbosCount, resourcesCount);
+	// Init shader buffers
+	initBuffers(init.m_uniformBuffers, m_ubos, m_ubosCount, resourcesCount,
+		dynCount);
+	initBuffers(init.m_storageBuffers, m_ssbos, m_ssbosCount, resourcesCount,
+		dynCount);
 
 	// Init vert buffers
 	m_vertBindingsCount = 0;
@@ -96,6 +109,8 @@ void ResourceGroupImpl::create(const ResourceGroupInitializer& init)
 		const BufferBinding& binding = init.m_vertexBuffers[i];
 		if(binding.m_buffer.isCreated())
 		{
+			ANKI_ASSERT(!binding.m_dynamic);
+
 			m_vertBuffNames[i] =
 				binding.m_buffer->getImplementation().getGlName();
 			m_vertBuffOffsets[i] = binding.m_offset;
@@ -103,6 +118,19 @@ void ResourceGroupImpl::create(const ResourceGroupInitializer& init)
 			++m_vertBindingsCount;
 			++resourcesCount;
 		}
+		else if(binding.m_dynamic)
+		{
+			++dynCount;
+			const GlState& state = getManager().getImplementation().
+				getRenderingThread().getState();
+
+			m_vertBuffNames[i] =
+				state.m_dynamicBuffers[BufferUsage::VERTEX].m_name;
+			m_vertBuffOffsets[i] = MAX_U32;
+
+			++m_vertBindingsCount;
+			m_hasDynamicVertexBuff = true;
+		}
 		else
 		{
 			m_vertBuffNames[i] = 0;
@@ -123,7 +151,7 @@ void ResourceGroupImpl::create(const ResourceGroupInitializer& init)
 		++resourcesCount;
 	}
 
-	ANKI_ASSERT(resourcesCount > 0 && "Resource group empty");
+	ANKI_ASSERT((resourcesCount > 0 || dynCount > 0) && "Resource group empty");
 
 	// Hold references
 	initResourceReferences(init, resourcesCount);
@@ -186,7 +214,8 @@ void ResourceGroupImpl::initResourceReferences(
 }
 
 //==============================================================================
-void ResourceGroupImpl::bind(U slot, GlState& state)
+void ResourceGroupImpl::bind(U slot, const DynamicBufferInfo& dynInfo,
+	GlState& state)
 {
 	ANKI_ASSERT(slot < MAX_RESOURCE_GROUPS);
 
@@ -212,27 +241,98 @@ void ResourceGroupImpl::bind(U slot, GlState& state)
 	for(U i = 0; i < m_ubosCount; ++i)
 	{
 		const auto& binding = m_ubos[i];
-		glBindBufferRange(GL_UNIFORM_BUFFER,
-			MAX_UNIFORM_BUFFER_BINDINGS * slot + i, binding.m_name,
-			binding.m_offset, binding.m_range);
+		if(binding.m_name == MAX_U32)
+		{
+			// Dynamic
+			DynamicBufferToken token = dynInfo.m_uniformBuffers[i];
+			ANKI_ASSERT(token.m_range != 0);
+
+			if(token.m_range != MAX_U32)
+			{
+				glBindBufferRange(GL_UNIFORM_BUFFER,
+					MAX_UNIFORM_BUFFER_BINDINGS * slot + i,
+					state.m_dynamicBuffers[BufferUsage::UNIFORM].m_name,
+					token.m_offset, token.m_range);
+			}
+			else
+			{
+				// It's invalid
+			}
+		}
+		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];
-		glBindBufferRange(GL_SHADER_STORAGE_BUFFER,
-			MAX_STORAGE_BUFFER_BINDINGS * slot + i, binding.m_name,
-			binding.m_offset, binding.m_range);
+		if(binding.m_name == MAX_U32)
+		{
+			// Dynamic
+			DynamicBufferToken token = dynInfo.m_storageBuffers[i];
+			ANKI_ASSERT(token.m_range != 0);
+
+			if(token.m_range != MAX_U32)
+			{
+				glBindBufferRange(GL_SHADER_STORAGE_BUFFER,
+					MAX_STORAGE_BUFFER_BINDINGS * slot + i,
+					state.m_dynamicBuffers[BufferUsage::STORAGE].m_name,
+					token.m_offset, token.m_range);
+			}
+			else
+			{
+				// It's invalid
+			}
+		}
+		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);
+		}
 	}
 
 	// Vertex buffers
 	if(m_vertBindingsCount)
 	{
 		ANKI_ASSERT(slot == 0 && "Only slot 0 can have vertex buffers");
-		glBindVertexBuffers(
-			0, m_vertBindingsCount, &m_vertBuffNames[0],
-			&m_vertBuffOffsets[0], &state.m_vertexBindingStrides[0]);
+
+		if(!m_hasDynamicVertexBuff)
+		{
+			glBindVertexBuffers(
+				0, m_vertBindingsCount, &m_vertBuffNames[0],
+				&m_vertBuffOffsets[0], &state.m_vertexBindingStrides[0]);
+		}
+		else
+		{
+			// Copy the offsets
+			Array<GLintptr, MAX_VERTEX_ATTRIBUTES> offsets = m_vertBuffOffsets;
+			for(U i = 0; i < MAX_VERTEX_ATTRIBUTES; ++i)
+			{
+				if(offsets[i] == MAX_U32)
+				{
+					// It's dynamic
+					ANKI_ASSERT(dynInfo.m_vertexBuffers[i].m_range != 0);
+					offsets[i] = dynInfo.m_vertexBuffers[i].m_offset;
+				}
+				else
+				{
+					ANKI_ASSERT(dynInfo.m_vertexBuffers[i].m_range == 0);
+				}
+			}
+
+			// Bind
+			glBindVertexBuffers(
+				0, m_vertBindingsCount, &m_vertBuffNames[0],
+				&offsets[0], &state.m_vertexBindingStrides[0]);
+		}
 	}
 
 	// Index buffer

+ 3 - 4
src/renderer/Bloom.cpp

@@ -132,7 +132,6 @@ Error Bloom::initInternal(const ConfigSet& config)
 	descInit.m_uniformBuffers[0].m_range = sizeof(Vec4);
 	descInit.m_storageBuffers[0].m_buffer =
 		m_r->getPps().getTm().getAverageLuminanceBuffer();
-	descInit.m_storageBuffers[0].m_range = sizeof(Vec4);
 
 	m_firstDescrGroup = gl.newInstance<ResourceGroup>(descInit);
 
@@ -180,7 +179,7 @@ void Bloom::run(CommandBufferPtr& cmdb)
 		m_commonUboUpdateTimestamp = getGlobalTimestamp();
 	}
 
-	cmdb->bindResourceGroup(m_firstDescrGroup, 0);
+	cmdb->bindResourceGroup(m_firstDescrGroup, 0, nullptr);
 
 	m_r->drawQuad(cmdb);
 
@@ -189,13 +188,13 @@ void Bloom::run(CommandBufferPtr& cmdb)
 	{
 		// hpass
 		cmdb->bindFramebuffer(m_hblurFb);
-		cmdb->bindResourceGroup(m_hDescrGroup, 0);
+		cmdb->bindResourceGroup(m_hDescrGroup, 0, nullptr);
 		cmdb->bindPipeline(m_hblurPpline);
 		m_r->drawQuad(cmdb);
 
 		// vpass
 		cmdb->bindFramebuffer(m_vblurFb);
-		cmdb->bindResourceGroup(m_vDescrGroup, 0);
+		cmdb->bindResourceGroup(m_vDescrGroup, 0, nullptr);
 		cmdb->bindPipeline(m_vblurPpline);
 		m_r->drawQuad(cmdb);
 	}

+ 1 - 1
src/renderer/DebugDrawer.cpp

@@ -178,7 +178,7 @@ Error DebugDrawer::flushInternal(PrimitiveTopology topology)
 		m_cmdb->bindPipeline(m_pplineLinesNoDepth);
 	}
 
-	m_cmdb->bindResourceGroup(m_rcGroup, 0);
+	m_cmdb->bindResourceGroup(m_rcGroup, 0, nullptr);
 	m_cmdb->drawArrays(clientVerts);
 
 	return ErrorCode::NONE;

+ 5 - 2
src/renderer/Drawer.cpp

@@ -36,6 +36,7 @@ struct RenderContext
 	Array<Mat4, MAX_INSTANCES> m_cachedTrfs;
 	U m_cachedTrfCount = 0;
 	F32 m_flod;
+	DynamicBufferInfo m_dynBufferInfo;
 };
 
 /// Visitor that sets a uniform
@@ -246,8 +247,9 @@ void RenderableDrawer::setupUniforms(RenderContext& ctx,
 	ctx.m_variant = &variant;
 
 	// Get some memory for uniforms
-	U8* uniforms = nullptr;
-	ctx.m_cmdb->updateDynamicUniforms(variant.getDefaultBlockSize(), uniforms);
+	U8* uniforms = ctx.m_cmdb->allocateDynamicMemory<U8>(
+		variant.getDefaultBlockSize(), BufferUsage::UNIFORM,
+		ctx.m_dynBufferInfo.m_uniformBuffers[0]);
 
 	// Call the visitor
 	SetupRenderableVariableVisitor visitor;
@@ -388,6 +390,7 @@ Error RenderableDrawer::renderSingle(RenderContext& ctx)
 	build.m_subMeshIndicesArray = &ctx.m_visibleNode->m_spatialIndices[0];
 	build.m_subMeshIndicesCount = ctx.m_visibleNode->m_spatialsCount;
 	build.m_cmdb = ctx.m_cmdb;
+	build.m_dynamicBufferInfo = &ctx.m_dynBufferInfo;
 
 	ANKI_CHECK(renderable.buildRendering(build));
 

+ 20 - 18
src/renderer/Fs.cpp

@@ -3,12 +3,12 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#include "anki/renderer/Fs.h"
-#include "anki/renderer/Renderer.h"
-#include "anki/renderer/Ms.h"
-#include "anki/renderer/Is.h"
-#include "anki/scene/SceneGraph.h"
-#include "anki/scene/Camera.h"
+#include <anki/renderer/Fs.h>
+#include <anki/renderer/Renderer.h>
+#include <anki/renderer/Ms.h>
+#include <anki/renderer/Is.h>
+#include <anki/scene/SceneGraph.h>
+#include <anki/scene/Camera.h>
 
 namespace anki {
 
@@ -30,7 +30,6 @@ Error Fs::init(const ConfigSet&)
 	m_fb = getGrManager().newInstance<Framebuffer>(fbInit);
 
 	// Init the global resources
-	for(U i = 0; i < m_globalResources.getSize(); ++i)
 	{
 		ResourceGroupInitializer init;
 		init.m_textures[0].m_texture = m_r->getMs().getDepthRt();
@@ -39,17 +38,13 @@ Error Fs::init(const ConfigSet&)
 		init.m_textures[2].m_texture =
 			m_r->getIs().getSm().getOmniTextureArray();
 
-		init.m_storageBuffers[0].m_buffer =
-			m_r->getIs().getCommonVarsBuffer(i);
-		init.m_storageBuffers[1].m_buffer =
-			m_r->getIs().getPointLightsBuffer(i);
-		init.m_storageBuffers[2].m_buffer =
-			m_r->getIs().getSpotLightsBuffer(i);
-		init.m_storageBuffers[3].m_buffer = m_r->getIs().getClusterBuffer(i);
-		init.m_storageBuffers[4].m_buffer =
-			m_r->getIs().getLightIndicesBuffer(i);
+		init.m_storageBuffers[0].m_dynamic = true;
+		init.m_storageBuffers[1].m_dynamic = true;
+		init.m_storageBuffers[2].m_dynamic = true;
+		init.m_storageBuffers[3].m_dynamic = true;
+		init.m_storageBuffers[4].m_dynamic = true;
 
-		m_globalResources[i] = getGrManager().newInstance<ResourceGroup>(init);
+		m_globalResources = getGrManager().newInstance<ResourceGroup>(init);
 	}
 
 	return ErrorCode::NONE;
@@ -62,7 +57,14 @@ Error Fs::run(CommandBufferPtr& cmdb)
 
 	FrustumComponent& camFr = m_r->getActiveFrustumComponent();
 
-	cmdb->bindResourceGroup(m_globalResources[getGlobalTimestamp() % 3], 1);
+	DynamicBufferInfo dyn;
+	dyn.m_storageBuffers[0] = m_r->getIs().getCommonVarsToken();
+	dyn.m_storageBuffers[1] = m_r->getIs().getPointLightsToken();
+	dyn.m_storageBuffers[2] = m_r->getIs().getSpotLightsToken();
+	dyn.m_storageBuffers[3] = m_r->getIs().getClustersToken();
+	dyn.m_storageBuffers[4] = m_r->getIs().getLightIndicesToken();
+
+	cmdb->bindResourceGroup(m_globalResources, 1, &dyn);
 
 	SArray<CommandBufferPtr> cmdbs(&cmdb, 1);
 	ANKI_CHECK(m_r->getSceneDrawer().render(

+ 7 - 16
src/renderer/Ir.cpp

@@ -88,19 +88,11 @@ Error Ir::init(const ConfigSet& initializer)
 
 	m_cubemapArr = getGrManager().newInstance<Texture>(texinit);
 
-	// Init buffers
-	m_probesBuffSize = sizeof(ShaderReflectionProbe) * m_cubemapArrSize;
-	for(U i = 0; i < m_probesBuff.getSize(); ++i)
-	{
-		m_probesBuff[i] = getGrManager().newInstance<Buffer>(m_probesBuffSize,
-			BufferUsageBit::STORAGE, BufferAccessBit::CLIENT_MAP_WRITE);
-	}
-
 	return ErrorCode::NONE;
 }
 
 //==============================================================================
-Error Ir::run()
+Error Ir::run(CommandBufferPtr cmdb)
 {
 	FrustumComponent& frc = m_r->getActiveFrustumComponent();
 	VisibilityTestResults& visRez = frc.getVisibilityTestResults();
@@ -111,13 +103,14 @@ Error Ir::run()
 		return ErrorCode::NONE;
 	}
 
-	BufferPtr buff =
-		m_probesBuff[getGlobalTimestamp() % m_probesBuff.getSize()];
-	ShaderReflectionProbe* probes = static_cast<ShaderReflectionProbe*>(
-		buff->map(0,m_probesBuffSize, BufferAccessBit::CLIENT_MAP_WRITE));
-
 	const VisibleNode* it = visRez.getReflectionProbesBegin();
 	const VisibleNode* end = visRez.getReflectionProbesEnd();
+	U probCount = end - it;
+
+	ShaderReflectionProbe* probes =
+		cmdb->allocateDynamicMemory<ShaderReflectionProbe>(probCount,
+		BufferUsage::UNIFORM, m_probesToken);
+
 	while(it != end)
 	{
 		ANKI_CHECK(renderReflection(*it->m_node, *probes));
@@ -125,8 +118,6 @@ Error Ir::run()
 		++probes;
 	}
 
-	buff->unmap();
-
 	return ErrorCode::NONE;
 }
 

+ 55 - 101
src/renderer/Is.cpp

@@ -290,43 +290,8 @@ Error Is::initInternal(const ConfigSet& config)
 	m_fb = getGrManager().newInstance<Framebuffer>(fbInit);
 
 	//
-	// Create UBOs
+	// Create resource group
 	//
-	m_pLightsBuffSize = m_maxPointLights * sizeof(ShaderPointLight);
-	m_sLightsBuffSize = m_maxSpotLights * sizeof(ShaderSpotLight);
-
-	for(U i = 0; i < m_pLightsBuffs.getSize(); ++i)
-	{
-		// Common variables
-		m_commonVarsBuffs[i] = getGrManager().newInstance<Buffer>(
-			sizeof(ShaderCommonUniforms), BufferUsageBit::STORAGE,
-			BufferAccessBit::CLIENT_MAP_WRITE);
-
-		// Point lights
-		m_pLightsBuffs[i] = getGrManager().newInstance<Buffer>(
-			m_pLightsBuffSize, BufferUsageBit::STORAGE,
-			BufferAccessBit::CLIENT_MAP_WRITE);
-
-		// Spot lights
-		m_sLightsBuffs[i] = getGrManager().newInstance<Buffer>(
-			m_sLightsBuffSize, BufferUsageBit::STORAGE,
-			BufferAccessBit::CLIENT_MAP_WRITE);
-
-		// Clusters
-		m_clusterBuffers[i] = getGrManager().newInstance<Buffer>(
-			m_r->getClusterCount() * sizeof(ShaderCluster),
-			BufferUsageBit::STORAGE, BufferAccessBit::CLIENT_MAP_WRITE);
-
-		// Index
-		m_lightIdsBuffers[i] = getGrManager().newInstance<Buffer>(
-			m_maxLightIds * sizeof(Lid),
-			BufferUsageBit::STORAGE, BufferAccessBit::CLIENT_MAP_WRITE);
-	}
-
-	//
-	// Create resource groups
-	//
-	for(U i = 0; i < m_rcGroups.getSize(); ++i)
 	{
 		ResourceGroupInitializer init;
 		init.m_textures[0].m_texture = m_r->getMs().getRt0();
@@ -336,13 +301,13 @@ Error Is::initInternal(const ConfigSet& config)
 		init.m_textures[4].m_texture = m_sm.getSpotTextureArray();
 		init.m_textures[5].m_texture = m_sm.getOmniTextureArray();
 
-		init.m_storageBuffers[0].m_buffer = m_commonVarsBuffs[i];
-		init.m_storageBuffers[1].m_buffer = m_pLightsBuffs[i];
-		init.m_storageBuffers[2].m_buffer = m_sLightsBuffs[i];
-		init.m_storageBuffers[3].m_buffer = m_clusterBuffers[i];
-		init.m_storageBuffers[4].m_buffer = m_lightIdsBuffers[i];
+		init.m_storageBuffers[0].m_dynamic = true;
+		init.m_storageBuffers[1].m_dynamic = true;
+		init.m_storageBuffers[2].m_dynamic = true;
+		init.m_storageBuffers[3].m_dynamic = true;
+		init.m_storageBuffers[4].m_dynamic = true;
 
-		m_rcGroups[i] = getGrManager().newInstance<ResourceGroup>(init);
+		m_rcGroup = getGrManager().newInstance<ResourceGroup>(init);
 	}
 
 	//
@@ -356,7 +321,7 @@ Error Is::initInternal(const ConfigSet& config)
 }
 
 //==============================================================================
-Error Is::lightPass(CommandBufferPtr& cmdBuff)
+Error Is::lightPass(CommandBufferPtr& cmdb)
 {
 	ThreadPool& threadPool = m_r->getThreadPool();
 	m_frc = &m_r->getActiveFrustumComponent();
@@ -419,7 +384,7 @@ Error Is::lightPass(CommandBufferPtr& cmdBuff)
 	ANKI_CHECK(m_sm.run(
 		{&spotCasters[0], spotCastersCount},
 		{&omniCasters[0], omniCastersCount},
-		cmdBuff));
+		cmdb));
 
 	//
 	// Write the lights and tiles UBOs
@@ -430,22 +395,28 @@ Error Is::lightPass(CommandBufferPtr& cmdBuff)
 
 	if(visiblePointLightsCount)
 	{
-		void* data = m_pLightsBuffs[m_currentFrame]->map(
-			0, visiblePointLightsCount * sizeof(ShaderPointLight),
-			BufferAccessBit::CLIENT_MAP_WRITE);
+		ShaderPointLight* data = cmdb->allocateDynamicMemory<ShaderPointLight>(
+			visiblePointLightsCount, BufferUsage::STORAGE, m_pLightsToken);
 
-		taskData.m_pointLights = SArray<ShaderPointLight>(
-			static_cast<ShaderPointLight*>(data), visiblePointLightsCount);
+		taskData.m_pointLights = SArray<ShaderPointLight>(data,
+			visiblePointLightsCount);
+	}
+	else
+	{
+		m_pLightsToken.invalidate();
 	}
 
 	if(visibleSpotLightsCount)
 	{
-		void* data = m_sLightsBuffs[m_currentFrame]->map(
-			0, visibleSpotLightsCount * sizeof(ShaderSpotLight),
-			BufferAccessBit::CLIENT_MAP_WRITE);
+		ShaderSpotLight* data = cmdb->allocateDynamicMemory<ShaderSpotLight>(
+			visibleSpotLightsCount, BufferUsage::STORAGE, m_sLightsToken);
 
-		taskData.m_spotLights = SArray<ShaderSpotLight>(
-			static_cast<ShaderSpotLight*>(data), visibleSpotLightsCount);
+		taskData.m_spotLights = SArray<ShaderSpotLight>(data,
+			visibleSpotLightsCount);
+	}
+	else
+	{
+		m_sLightsToken.invalidate();
 	}
 
 	taskData.m_lightsBegin = vi.getLightsBegin();
@@ -453,22 +424,17 @@ Error Is::lightPass(CommandBufferPtr& cmdBuff)
 
 	taskData.m_is = this;
 
-	// Map clusters
-	void* data = m_clusterBuffers[m_currentFrame]->map(
-		0,
-		clusterCount * sizeof(ShaderCluster),
-		BufferAccessBit::CLIENT_MAP_WRITE);
+	// Get mem for clusters
+	ShaderCluster* data = cmdb->allocateDynamicMemory<ShaderCluster>(
+		clusterCount, BufferUsage::STORAGE, m_clustersToken);
 
-	taskData.m_clusters = SArray<ShaderCluster>(
-		static_cast<ShaderCluster*>(data), clusterCount);
+	taskData.m_clusters = SArray<ShaderCluster>(data, clusterCount);
 
-	// Map light IDs
-	data = m_lightIdsBuffers[m_currentFrame]->map(
-		0,
-		m_maxLightIds * sizeof(Lid),
-		BufferAccessBit::CLIENT_MAP_WRITE);
+	// Allocate light IDs
+	Lid* data2 = cmdb->allocateDynamicMemory<Lid>(m_maxLightIds,
+		BufferUsage::STORAGE, m_lightIdsToken);
 
-	taskData.m_lightIds = SArray<Lid>(static_cast<Lid*>(data), m_maxLightIds);
+	taskData.m_lightIds = SArray<Lid>(data2, m_maxLightIds);
 
 	for(U i = 0; i < threadPool.getThreadsCount(); i++)
 	{
@@ -477,35 +443,19 @@ Error Is::lightPass(CommandBufferPtr& cmdBuff)
 		threadPool.assignNewTask(i, &tasks[i]);
 	}
 
-	// In the meantime set the state
-	setState(cmdBuff);
-
 	// Update uniforms
-	updateCommonBlock(cmdBuff, *m_frc);
+	updateCommonBlock(cmdb, *m_frc);
+
+	// In the meantime set the state
+	setState(cmdb);
 
 	// Sync
 	ANKI_CHECK(threadPool.waitForAllThreadsToFinish());
 
-	//
-	// Unmap
-	//
-	if(visiblePointLightsCount)
-	{
-		m_pLightsBuffs[m_currentFrame]->unmap();
-	}
-
-	if(visibleSpotLightsCount)
-	{
-		m_sLightsBuffs[m_currentFrame]->unmap();
-	}
-
-	m_clusterBuffers[m_currentFrame]->unmap();
-	m_lightIdsBuffers[m_currentFrame]->unmap();
-
 	//
 	// Draw
 	//
-	cmdBuff->drawArrays(4, m_r->getTileCount());
+	cmdb->drawArrays(4, m_r->getTileCount());
 
 	return ErrorCode::NONE;
 }
@@ -770,29 +720,35 @@ void Is::binLight(
 }
 
 //==============================================================================
-void Is::setState(CommandBufferPtr& cmdBuff)
+void Is::setState(CommandBufferPtr& cmdb)
 {
-	cmdBuff->bindFramebuffer(m_fb);
-	cmdBuff->setViewport(0, 0, m_r->getWidth(), m_r->getHeight());
-	cmdBuff->bindPipeline(m_lightPpline);
-	cmdBuff->bindResourceGroup(m_rcGroups[m_currentFrame], 0);
+	cmdb->bindFramebuffer(m_fb);
+	cmdb->setViewport(0, 0, m_r->getWidth(), m_r->getHeight());
+	cmdb->bindPipeline(m_lightPpline);
+
+	DynamicBufferInfo dyn;
+	dyn.m_storageBuffers[0] = m_commonVarsToken;
+	dyn.m_storageBuffers[1] = m_pLightsToken;
+	dyn.m_storageBuffers[2] = m_sLightsToken;
+	dyn.m_storageBuffers[3] = m_clustersToken;
+	dyn.m_storageBuffers[4] = m_lightIdsToken;
+
+	cmdb->bindResourceGroup(m_rcGroup, 0, &dyn);
 }
 
 //==============================================================================
-Error Is::run(CommandBufferPtr& cmdBuff)
+Error Is::run(CommandBufferPtr& cmdb)
 {
 	// Do the light pass including the shadow passes
-	return lightPass(cmdBuff);
+	return lightPass(cmdb);
 }
 
 //==============================================================================
 void Is::updateCommonBlock(CommandBufferPtr& cmdb, const FrustumComponent& fr)
 {
 	ShaderCommonUniforms* blk =
-		static_cast<ShaderCommonUniforms*>(
-			m_commonVarsBuffs[m_currentFrame]->map(
-			0, sizeof(ShaderCommonUniforms),
-			BufferAccessBit::CLIENT_MAP_WRITE));
+		cmdb->allocateDynamicMemory<ShaderCommonUniforms>(1,
+		BufferUsage::STORAGE, m_commonVarsToken);
 
 	// Start writing
 	blk->m_projectionParams = m_r->getProjectionParameters();
@@ -818,8 +774,6 @@ void Is::updateCommonBlock(CommandBufferPtr& cmdb, const FrustumComponent& fr)
 	}
 
 	blk->m_tileCount = UVec4(m_r->getTileCountXY(), m_r->getTileCount(), 0);
-
-	m_commonVarsBuffs[m_currentFrame]->unmap();
 }
 
 } // end namespace anki

+ 28 - 41
src/renderer/Lf.cpp

@@ -96,16 +96,6 @@ Error Lf::initSprite(const ConfigSet& config)
 //==============================================================================
 Error Lf::initOcclusion(const ConfigSet& config)
 {
-	// Init vert buff
-	m_positionsVertBuffSize = sizeof(Vec3) * m_maxFlares;
-
-	for(U i = 0; i < m_positionsVertBuff.getSize(); ++i)
-	{
-		m_positionsVertBuff[i] = getGrManager().newInstance<Buffer>(
-			m_positionsVertBuffSize, BufferUsageBit::VERTEX,
-			BufferAccessBit::CLIENT_MAP_WRITE);
-	}
-
 	// Shaders
 	ANKI_CHECK(getResourceManager().loadResource(
 		"shaders/LfOcclusion.vert.glsl", m_occlusionVert));
@@ -141,12 +131,11 @@ Error Lf::initOcclusion(const ConfigSet& config)
 	m_occlusionPpline = getGrManager().newInstance<Pipeline>(init);
 
 	// Init resource group
-	for(U i = 0; i < m_occlusionRcGroups.getSize(); ++i)
 	{
 		ResourceGroupInitializer rcInit;
-		rcInit.m_vertexBuffers[0].m_buffer = m_positionsVertBuff[i];
-		m_occlusionRcGroups[i] =
-			getGrManager().newInstance<ResourceGroup>(rcInit);
+		rcInit.m_vertexBuffers[0].m_dynamic = true;
+		rcInit.m_uniformBuffers[0].m_dynamic = true;
+		m_occlusionRcGroup = getGrManager().newInstance<ResourceGroup>(rcInit);
 	}
 
 	return ErrorCode::NONE;
@@ -168,33 +157,32 @@ void Lf::runOcclusionTests(CommandBufferPtr& cmdb)
 	FrustumComponent& camFr = m_r->getActiveFrustumComponent();
 	VisibilityTestResults& vi = camFr.getVisibilityTestResults();
 
+	if(vi.getLensFlaresCount() > m_maxFlares)
+	{
+		ANKI_LOGW("Visible flares exceed the limit. Increase lf.maxFlares");
+	}
+
 	U totalCount = min<U>(vi.getLensFlaresCount(), m_maxFlares);
 	if(totalCount > 0)
 	{
-		ANKI_ASSERT(sizeof(Vec3) * totalCount <= m_positionsVertBuffSize);
-
-		if(vi.getLensFlaresCount() > m_maxFlares)
-		{
-			ANKI_LOGW("Visible flares exceed the limit. Increase lf.maxFlares");
-		}
-
-		U frame = getGlobalTimestamp() % m_positionsVertBuff.getSize();
-
-		// Setup state
-		cmdb->bindPipeline(m_occlusionPpline);
-		cmdb->bindResourceGroup(m_occlusionRcGroups[frame], 0);
-
 		// Setup MVP UBO
-		Mat4* mvp = nullptr;
-		cmdb->updateDynamicUniforms(sizeof(Mat4), mvp);
+		DynamicBufferToken token;
+		Mat4* mvp = cmdb->allocateDynamicMemory<Mat4>(1, BufferUsage::UNIFORM,
+			token);
 		*mvp = camFr.getViewProjectionMatrix();
 
-		// Allocate vertices and fire write job
-		BufferPtr& positionsVertBuff = m_positionsVertBuff[frame];
+		// Alloc dyn mem
+		DynamicBufferToken token2;
+		Vec3* positions = cmdb->allocateDynamicMemory<Vec3>(totalCount,
+			BufferUsage::VERTEX, token2);
+		const Vec3* initialPositions = positions;
 
-		Vec3* positions = static_cast<Vec3*>(positionsVertBuff->map(
-			0, sizeof(Vec3) * totalCount, BufferAccessBit::CLIENT_MAP_WRITE));
-		Vec3* initialPositions = positions;
+		// Setup state
+		cmdb->bindPipeline(m_occlusionPpline);
+		DynamicBufferInfo dyn;
+		dyn.m_uniformBuffers[0] = token;
+		dyn.m_vertexBuffers[0] = token2;
+		cmdb->bindResourceGroup(m_occlusionRcGroup, 0, &dyn);
 
 		// Iterate lens flare
 		auto it = vi.getLensFlaresBegin();
@@ -217,8 +205,6 @@ void Lf::runOcclusionTests(CommandBufferPtr& cmdb)
 			++positions;
 		}
 
-		positionsVertBuff->unmap();
-
 		ANKI_ASSERT(positions == initialPositions + totalCount);
 	}
 }
@@ -258,12 +244,10 @@ void Lf::run(CommandBufferPtr& cmdb)
 			U count = 0;
 			U spritesCount = max<U>(1, m_maxSpritesPerFlare); // TODO
 
-			cmdb->bindResourceGroup(lf.getResourceGroup(), 0);
-
 			// Get uniform memory
-			Sprite* tmpSprites = nullptr;
-			cmdb->updateDynamicUniforms(
-				sizeof(Sprite) * spritesCount, tmpSprites);
+			DynamicBufferToken token;
+			Sprite* tmpSprites = cmdb->allocateDynamicMemory<Sprite>(
+				spritesCount, BufferUsage::UNIFORM, token);
 			SArray<Sprite> sprites(tmpSprites, spritesCount);
 
 			// misc
@@ -289,6 +273,9 @@ void Lf::run(CommandBufferPtr& cmdb)
 
 			if(!queryInvalid)
 			{
+				DynamicBufferInfo dyn;
+				dyn.m_uniformBuffers[0] = token;
+				cmdb->bindResourceGroup(lf.getResourceGroup(), 0, &dyn);
 				cmdb->drawArraysConditional(query, 4);
 			}
 			else

+ 1 - 1
src/renderer/MainRenderer.cpp

@@ -147,7 +147,7 @@ Error MainRenderer::render(SceneGraph& scene)
 		cmdb->setViewport(0, 0, m_width, m_height);
 
 		cmdb->bindPipeline(m_blitPpline);
-		cmdb->bindResourceGroup(m_rcGroup, 0);
+		cmdb->bindResourceGroup(m_rcGroup, 0, nullptr);
 
 		m_r->drawQuad(cmdb);
 	}

+ 1 - 1
src/renderer/Pps.cpp

@@ -210,7 +210,7 @@ void Pps::run(CommandBufferPtr& cmdb)
 	cmdb->bindFramebuffer(fb);
 	cmdb->setViewport(0, 0, width, height);
 	cmdb->bindPipeline(m_ppline);
-	cmdb->bindResourceGroup(m_rcGroup, 0);
+	cmdb->bindResourceGroup(m_rcGroup, 0, nullptr);
 
 	if(m_uniformsDirty)
 	{

+ 1 - 1
src/renderer/Renderer.cpp

@@ -170,7 +170,7 @@ Error Renderer::render(SceneNode& frustumableNode, U frustumIdx,
 	// Run reflection passes
 	if(m_ir.isCreated())
 	{
-		ANKI_CHECK(m_ir->run());
+		ANKI_CHECK(m_ir->run(cmdb[0]));
 	}
 
 	ANKI_COUNTER_START_TIMER(RENDERER_MS_TIME);

+ 3 - 3
src/renderer/Ssao.cpp

@@ -267,7 +267,7 @@ void Ssao::run(CommandBufferPtr& cmdb)
 	cmdb->bindFramebuffer(m_vblurFb);
 	cmdb->setViewport(0, 0, m_width, m_height);
 	cmdb->bindPipeline(m_ssaoPpline);
-	cmdb->bindResourceGroup(m_rcFirst, 0);
+	cmdb->bindResourceGroup(m_rcFirst, 0, nullptr);
 
 	// Write common block
 	const FrustumComponent& camFr = m_r->getActiveFrustumComponent();
@@ -298,13 +298,13 @@ void Ssao::run(CommandBufferPtr& cmdb)
 		// hpass
 		cmdb->bindFramebuffer(m_hblurFb);
 		cmdb->bindPipeline(m_hblurPpline);
-		cmdb->bindResourceGroup(m_hblurRc, 0);
+		cmdb->bindResourceGroup(m_hblurRc, 0, nullptr);
 		m_r->drawQuad(cmdb);
 
 		// vpass
 		cmdb->bindFramebuffer(m_vblurFb);
 		cmdb->bindPipeline(m_vblurPpline);
-		cmdb->bindResourceGroup(m_vblurRc, 0);
+		cmdb->bindResourceGroup(m_vblurRc, 0, nullptr);
 		m_r->drawQuad(cmdb);
 	}
 }

+ 1 - 1
src/renderer/Sslf.cpp

@@ -89,7 +89,7 @@ void Sslf::run(CommandBufferPtr& cmdb)
 		m_r->getPps().getBloom().getHeight());
 
 	cmdb->bindPipeline(m_ppline);
-	cmdb->bindResourceGroup(m_rcGroup, 0);
+	cmdb->bindResourceGroup(m_rcGroup, 0, nullptr);
 
 	m_r->drawQuad(cmdb);
 }

+ 2 - 2
src/renderer/Sslr.cpp

@@ -110,7 +110,7 @@ void Sslr::run(CommandBufferPtr& cmdBuff)
 	cmdBuff->bindFramebuffer(m_fb);
 	cmdBuff->setViewport(0, 0, m_width, m_height);
 	cmdBuff->bindPipeline(m_reflectionPpline);
-	cmdBuff->bindResourceGroup(m_rcGroup, 0);
+	cmdBuff->bindResourceGroup(m_rcGroup, 0, nullptr);
 
 	m_r->drawQuad(cmdBuff);
 
@@ -119,7 +119,7 @@ void Sslr::run(CommandBufferPtr& cmdBuff)
 	cmdBuff->bindFramebuffer(m_isFb);
 	cmdBuff->bindPipeline(m_blitPpline);
 	cmdBuff->setViewport(0, 0, m_r->getWidth(), m_r->getHeight());
-	cmdBuff->bindResourceGroup(m_rcGroupBlit, 0);
+	cmdBuff->bindResourceGroup(m_rcGroupBlit, 0, nullptr);
 
 	m_r->drawQuad(cmdBuff);
 }

+ 1 - 1
src/renderer/Tiler.cpp

@@ -89,7 +89,7 @@ void Tiler::run(CommandBufferPtr& cmd)
 	U pboIdx = getGlobalTimestamp() % m_outBuffers.getSize();
 
 	cmd->bindPipeline(m_ppline);
-	cmd->bindResourceGroup(m_rcGroups[pboIdx], 0);
+	cmd->bindResourceGroup(m_rcGroups[pboIdx], 0, nullptr);
 
 	cmd->dispatchCompute(
 		m_r->getTileCountXY().x(), m_r->getTileCountXY().y(), 1);

+ 1 - 1
src/renderer/Tm.cpp

@@ -57,7 +57,7 @@ Error Tm::create(const ConfigSet& initializer)
 void Tm::run(CommandBufferPtr& cmdb)
 {
 	cmdb->bindPipeline(m_luminancePpline);
-	cmdb->bindResourceGroup(m_rcGroup, 0);
+	cmdb->bindResourceGroup(m_rcGroup, 0, nullptr);
 
 	cmdb->dispatchCompute(1, 1, 1);
 }

+ 2 - 0
src/resource/Material.cpp

@@ -441,6 +441,8 @@ Error Material::createProgramSourceToCache(
 //==============================================================================
 void Material::fillResourceGroupInitializer(ResourceGroupInitializer& rcinit)
 {
+	rcinit.m_uniformBuffers[0].m_dynamic = true;
+
 	UpdateTexturesVisitor visitor;
 	visitor.m_init = &rcinit;
 	visitor.m_manager = &getManager();

+ 1 - 0
src/scene/LensFlareComponent.cpp

@@ -37,6 +37,7 @@ Error LensFlareComponent::create(const CString& textureFilename)
 	// Resource group
 	ResourceGroupInitializer rcInit;
 	rcInit.m_textures[0].m_texture = m_tex->getGrTexture();
+	rcInit.m_uniformBuffers[0].m_dynamic = true;
 	m_rcGroup = gr.newInstance<ResourceGroup>(rcInit);
 
 	return ErrorCode::NONE;

+ 1 - 1
src/scene/ModelNode.cpp

@@ -115,7 +115,7 @@ Error ModelPatchNode::buildRendering(RenderingBuildInfo& data) const
 
 	// Set jobs
 	data.m_cmdb->bindPipeline(ppline);
-	data.m_cmdb->bindResourceGroup(grResources, 0);
+	data.m_cmdb->bindResourceGroup(grResources, 0, data.m_dynamicBufferInfo);
 
 	// Drawcall
 	U32 offset = indicesOffsetArray[0] / sizeof(U16);

+ 2 - 1
src/scene/ParticleEmitter.cpp

@@ -365,7 +365,8 @@ Error ParticleEmitter::buildRendering(RenderingBuildInfo& data) const
 
 	U frame = (getGlobalTimestamp() % 3);
 
-	data.m_cmdb->bindResourceGroup(m_grGroups[frame], 0);
+	data.m_cmdb->bindResourceGroup(m_grGroups[frame], 0,
+		data.m_dynamicBufferInfo);
 
 	data.m_cmdb->drawArrays(m_aliveParticlesCount, data.m_subMeshIndicesCount);
 

+ 2 - 1
src/scene/StaticGeometryNode.cpp

@@ -97,7 +97,8 @@ Error StaticGeometryPatchNode::buildRendering(RenderingBuildInfo& data) const
 
 	if(drawCount == 1)
 	{
-		data.m_cmdb->bindResourceGroup(grResources, 0);
+		data.m_cmdb->bindResourceGroup(grResources, 0,
+			data.m_dynamicBufferInfo);
 
 		data.m_cmdb->drawElements(
 			indicesCountArray[0],

+ 1 - 1
src/ui/UiInterfaceImpl.cpp

@@ -142,7 +142,7 @@ void UiInterfaceImpl::drawLines(const SArray<UVec2>& positions,
 
 	m_cmdb->bindPipeline(m_stages[StageId::LINES].m_ppline);
 	m_cmdb->bindResourceGroup(m_stages[StageId::LINES].m_rcGroups[m_timestamp],
-		0);
+		0, nullptr);
 	m_cmdb->drawArrays(positions.getSize(), 1, m_vertCounts[stageId]);
 
 	for(const UVec2& pos : positions)

+ 2 - 1
testapp/Main.cpp

@@ -95,6 +95,7 @@ Error init()
 		1.0));
 #endif
 
+	if(0)
 	{
 		ReflectionProbe* refl;
 		scene.newSceneNode<ReflectionProbe>("refl", refl, 6.0f);
@@ -480,7 +481,7 @@ Error initSubsystems(int argc, char* argv[])
 	config.set("is.sm.poissonEnabled", true);
 	config.set("is.sm.resolution", 1024);
 	config.set("lf.maxFlares", 32);
-	config.set("pps.enabled", false);
+	config.set("pps.enabled", true);
 	config.set("pps.bloom.enabled", true);
 	config.set("pps.bloom.renderingQuality", 0.5);
 	config.set("pps.bloom.blurringDist", 1.0);