浏览代码

Add caching support in graphics module. It's explicit

Panagiotis Christopoulos Charitos 9 年之前
父节点
当前提交
156eb7cb83

+ 3 - 0
include/anki/core/Trace.h

@@ -55,6 +55,9 @@ enum class TraceCounterType
 	GR_DYNAMIC_UNIFORMS_SIZE,
 	GR_DYNAMIC_UNIFORMS_SIZE,
 	GR_DYNAMIC_STORAGE_SIZE,
 	GR_DYNAMIC_STORAGE_SIZE,
 	GR_VERTICES,
 	GR_VERTICES,
+	GR_PIPELINES_CREATED,
+	GR_PIPELINE_BINDS_SKIPPED,
+	GR_PIPELINE_BINDS_HAPPENED,
 	RENDERER_LIGHTS,
 	RENDERER_LIGHTS,
 	RENDERER_SHADOW_PASSES,
 	RENDERER_SHADOW_PASSES,
 	RENDERER_MERGED_DRAWCALLS,
 	RENDERER_MERGED_DRAWCALLS,

+ 3 - 1
include/anki/gr/Buffer.h

@@ -17,8 +17,10 @@ namespace anki
 class Buffer : public GrObject
 class Buffer : public GrObject
 {
 {
 public:
 public:
+	static const GrObjectType CLASS_TYPE = GrObjectType::BUFFER;
+
 	/// Construct.
 	/// Construct.
-	Buffer(GrManager* manager);
+	Buffer(GrManager* manager, U64 hash = 0);
 
 
 	/// Destroy.
 	/// Destroy.
 	~Buffer();
 	~Buffer();

+ 3 - 1
include/anki/gr/CommandBuffer.h

@@ -91,8 +91,10 @@ public:
 class CommandBuffer : public GrObject
 class CommandBuffer : public GrObject
 {
 {
 public:
 public:
+	static const GrObjectType CLASS_TYPE = GrObjectType::COMMAND_BUFFER;
+
 	/// Construct.
 	/// Construct.
-	CommandBuffer(GrManager* manager);
+	CommandBuffer(GrManager* manager, U64 hash = 0);
 
 
 	/// Destroy.
 	/// Destroy.
 	~CommandBuffer();
 	~CommandBuffer();

+ 26 - 5
include/anki/gr/Common.h

@@ -14,6 +14,8 @@ namespace anki
 {
 {
 
 
 // Forward
 // Forward
+class GrObject;
+
 class GrManager;
 class GrManager;
 class GrManagerImpl;
 class GrManagerImpl;
 class TextureInitInfo;
 class TextureInitInfo;
@@ -25,10 +27,17 @@ class FramebufferInitInfo;
 class DynamicBufferToken;
 class DynamicBufferToken;
 class DynamicBufferInfo;
 class DynamicBufferInfo;
 
 
+/// @addtogroup graphics
+/// @{
+
+/// Smart pointer for resources.
+template<typename T>
+using GrObjectPtr = IntrusivePtr<T, DefaultPtrDeleter<T>>;
+
 #define ANKI_GR_CLASS(x_)                                                      \
 #define ANKI_GR_CLASS(x_)                                                      \
 	class x_##Impl;                                                            \
 	class x_##Impl;                                                            \
 	class x_;                                                                  \
 	class x_;                                                                  \
-	using x_##Ptr = IntrusivePtr<x_>;
+	using x_##Ptr = GrObjectPtr<x_>;
 
 
 ANKI_GR_CLASS(Buffer)
 ANKI_GR_CLASS(Buffer)
 ANKI_GR_CLASS(Texture)
 ANKI_GR_CLASS(Texture)
@@ -42,8 +51,20 @@ ANKI_GR_CLASS(ResourceGroup)
 
 
 #undef ANKI_GR_CLASS
 #undef ANKI_GR_CLASS
 
 
-/// @addtogroup graphics
-/// @{
+/// Graphics object type.
+enum GrObjectType : U16
+{
+	BUFFER,
+	COMMAND_BUFFER,
+	FRAMEBUFFER,
+	OCCLUSION_QUERY,
+	PIPELINE,
+	RESOURCE_GROUP,
+	SAMPLER,
+	SHADER,
+	TEXTURE,
+	COUNT
+};
 
 
 /// The type of the allocator for heap allocations
 /// The type of the allocator for heap allocations
 template<typename T>
 template<typename T>
@@ -110,8 +131,8 @@ const U MAX_TEXTURE_BINDINGS = 10;
 const U MAX_UNIFORM_BUFFER_BINDINGS = 4;
 const U MAX_UNIFORM_BUFFER_BINDINGS = 4;
 const U MAX_STORAGE_BUFFER_BINDINGS = 4;
 const U MAX_STORAGE_BUFFER_BINDINGS = 4;
 const U MAX_ATOMIC_BUFFER_BINDINGS = 1;
 const U MAX_ATOMIC_BUFFER_BINDINGS = 1;
-const U MAX_FRAMES_IN_FLIGHT = 3;
-const U MAX_RESOURCE_GROUPS = 2;
+const U MAX_FRAMES_IN_FLIGHT = 3; ///< Triple buffering.
+const U MAX_RESOURCE_GROUPS = 2; ///< Groups that can be bound at the same time.
 
 
 /// Compute max number of mipmaps for a 2D texture.
 /// Compute max number of mipmaps for a 2D texture.
 inline U computeMaxMipmapCount(U w, U h)
 inline U computeMaxMipmapCount(U w, U h)

+ 3 - 1
include/anki/gr/Framebuffer.h

@@ -87,8 +87,10 @@ public:
 class Framebuffer : public GrObject
 class Framebuffer : public GrObject
 {
 {
 public:
 public:
+	static const GrObjectType CLASS_TYPE = GrObjectType::FRAMEBUFFER;
+
 	/// Construct.
 	/// Construct.
-	Framebuffer(GrManager* manager);
+	Framebuffer(GrManager* manager, U64 hash = 0);
 
 
 	/// Destroy.
 	/// Destroy.
 	~Framebuffer();
 	~Framebuffer();

+ 53 - 4
include/anki/gr/GrManager.h

@@ -6,6 +6,8 @@
 #pragma once
 #pragma once
 
 
 #include <anki/gr/Common.h>
 #include <anki/gr/Common.h>
+#include <anki/gr/GrObjectCache.h>
+#include <anki/gr/GrObject.h>
 #include <anki/util/String.h>
 #include <anki/util/String.h>
 
 
 namespace anki
 namespace anki
@@ -47,6 +49,9 @@ class GrManager
 {
 {
 	friend class GrManagerImpl;
 	friend class GrManagerImpl;
 
 
+	template<typename>
+	friend class GrObjectPtrDeleter;
+
 public:
 public:
 	/// Default constructor
 	/// Default constructor
 	GrManager();
 	GrManager();
@@ -64,10 +69,15 @@ public:
 
 
 	/// Create a new graphics object.
 	/// Create a new graphics object.
 	template<typename T, typename... Args>
 	template<typename T, typename... Args>
-	IntrusivePtr<T> newInstance(Args&&... args);
+	GrObjectPtr<T> newInstance(Args&&... args);
+
+	/// Create a new graphics object and use the cache to avoid duplication.
+	/// It's thread safe.
+	template<typename T, typename TArg>
+	GrObjectPtr<T> newInstanceCached(const TArg& arg);
 
 
 	/// Allocate memory for dynamic buffers. The memory will be reclaimed at
 	/// Allocate memory for dynamic buffers. The memory will be reclaimed at
-	/// the begining of the next frame.
+	/// the begining of the N-(MAX_FRAMES_IN_FLIGHT-1) frame.
 	void* allocateFrameHostVisibleMemory(
 	void* allocateFrameHostVisibleMemory(
 		PtrSize size, BufferUsage usage, DynamicBufferToken& token);
 		PtrSize size, BufferUsage usage, DynamicBufferToken& token);
 
 
@@ -97,20 +107,59 @@ anki_internal:
 		return m_uuidIndex;
 		return m_uuidIndex;
 	}
 	}
 
 
+	void unregisterCachedObject(GrObject* ptr)
+	{
+		ANKI_ASSERT(ptr);
+		if(ptr->getHash() != 0)
+		{
+			GrObjectCache& cache = m_caches[ptr->getType()];
+			LockGuard<Mutex> lock(cache.getMutex());
+			cache.unregisterObject(ptr);
+		}
+	}
+
 private:
 private:
 	GrAllocator<U8> m_alloc; ///< Keep it first to get deleted last
 	GrAllocator<U8> m_alloc; ///< Keep it first to get deleted last
 	UniquePtr<GrManagerImpl> m_impl;
 	UniquePtr<GrManagerImpl> m_impl;
 	String m_cacheDir;
 	String m_cacheDir;
 	U64 m_uuidIndex = 1;
 	U64 m_uuidIndex = 1;
+	Array<GrObjectCache, U(GrObjectType::COUNT)> m_caches;
 };
 };
 
 
+//==============================================================================
 template<typename T, typename... Args>
 template<typename T, typename... Args>
-IntrusivePtr<T> GrManager::newInstance(Args&&... args)
+GrObjectPtr<T> GrManager::newInstance(Args&&... args)
 {
 {
-	IntrusivePtr<T> ptr(m_alloc.newInstance<T>(this));
+	const U64 hash = 0;
+	GrObjectPtr<T> ptr(m_alloc.newInstance<T>(this, hash));
 	ptr->init(args...);
 	ptr->init(args...);
 	return ptr;
 	return ptr;
 }
 }
+
+//==============================================================================
+template<typename T, typename TArg>
+GrObjectPtr<T> GrManager::newInstanceCached(const TArg& arg)
+{
+	GrObjectCache& cache = m_caches[T::CLASS_TYPE];
+	U64 hash = arg.computeHash();
+	ANKI_ASSERT(hash != 0);
+
+	LockGuard<Mutex> lock(cache.getMutex());
+	GrObject* ptr = cache.tryFind(hash);
+	if(ptr == nullptr)
+	{
+		T* tptr = m_alloc.newInstance<T>(this, hash);
+		tptr->init(arg);
+		ptr = tptr;
+		cache.registerObject(ptr);
+	}
+	else
+	{
+		ANKI_ASSERT(ptr->getType() == T::CLASS_TYPE);
+	}
+
+	return GrObjectPtr<T>(static_cast<T*>(ptr));
+}
 /// @}
 /// @}
 
 
 } // end namespace anki
 } // end namespace anki

+ 13 - 2
include/anki/gr/GrObject.h

@@ -19,10 +19,13 @@ namespace anki
 class GrObject : public NonCopyable
 class GrObject : public NonCopyable
 {
 {
 public:
 public:
-	GrObject(GrManager* manager);
+	GrObject(GrManager* manager, GrObjectType type, U64 hash);
 
 
-	virtual ~GrObject()
+	virtual ~GrObject();
+
+	GrObjectType getType() const
 	{
 	{
+		return m_type;
 	}
 	}
 
 
 	GrManager& getManager()
 	GrManager& getManager()
@@ -48,10 +51,18 @@ public:
 		return m_uuid;
 		return m_uuid;
 	}
 	}
 
 
+	/// Get a hash if it's part of a cache. If zero then it's not cached.
+	U64 getHash() const
+	{
+		return m_hash;
+	}
+
 private:
 private:
 	Atomic<I32> m_refcount;
 	Atomic<I32> m_refcount;
 	GrManager* m_manager;
 	GrManager* m_manager;
 	U64 m_uuid;
 	U64 m_uuid;
+	U64 m_hash;
+	GrObjectType m_type;
 };
 };
 /// @}
 /// @}
 
 

+ 85 - 0
include/anki/gr/GrObjectCache.h

@@ -0,0 +1,85 @@
+// 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/Common.h>
+#include <anki/gr/GrObject.h>
+#include <anki/util/HashMap.h>
+#include <anki/util/NonCopyable.h>
+
+namespace anki
+{
+
+/// @addtogroup graphics
+/// @{
+
+/// A cache that can be used for specific GrObject types.
+class GrObjectCache : public NonCopyable
+{
+public:
+	void init(const GrAllocator<U8>& alloc)
+	{
+		m_alloc = alloc;
+	}
+
+	Mutex& getMutex()
+	{
+		return m_mtx;
+	}
+
+	/// Try to find an object in the cache.
+	GrObject* tryFind(U64 hash)
+	{
+		auto it = m_map.find(hash);
+		return (it != m_map.getEnd()) ? *it : nullptr;
+	}
+
+	/// Register object in the cache.
+	void registerObject(GrObject* obj)
+	{
+		ANKI_ASSERT(obj->getHash() != 0);
+		ANKI_ASSERT(tryFind(obj->getHash()) == nullptr);
+		m_map.pushBack(m_alloc, obj->getHash(), obj);
+	}
+
+	/// Unregister an object from the cache.
+	void unregisterObject(GrObject* obj)
+	{
+		ANKI_ASSERT(tryFind(obj->getHash()) != nullptr);
+		auto it = m_map.find(obj->getHash());
+		m_map.erase(m_alloc, it);
+	}
+
+private:
+	using Key = U64;
+
+	/// Hash the hash.
+	class Hasher
+	{
+	public:
+		U64 operator()(const Key& b) const
+		{
+			return b;
+		}
+	};
+
+	/// Hash compare.
+	class Compare
+	{
+	public:
+		Bool operator()(const Key& a, const Key& b) const
+		{
+			return a == b;
+		}
+	};
+
+	GrAllocator<U8> m_alloc;
+	HashMap<U64, GrObject*, Hasher, Compare> m_map;
+	Mutex m_mtx;
+};
+/// @}
+
+} // end namespace anki

+ 3 - 1
include/anki/gr/OcclusionQuery.h

@@ -17,8 +17,10 @@ namespace anki
 class OcclusionQuery : public GrObject
 class OcclusionQuery : public GrObject
 {
 {
 public:
 public:
+	static const GrObjectType CLASS_TYPE = GrObjectType::OCCLUSION_QUERY;
+
 	/// Construct.
 	/// Construct.
-	OcclusionQuery(GrManager* manager);
+	OcclusionQuery(GrManager* manager, U64 hash = 0);
 
 
 	/// Destroy.
 	/// Destroy.
 	~OcclusionQuery();
 	~OcclusionQuery();

+ 28 - 4
include/anki/gr/Pipeline.h

@@ -7,6 +7,7 @@
 
 
 #include <anki/gr/GrObject.h>
 #include <anki/gr/GrObject.h>
 #include <anki/gr/Shader.h>
 #include <anki/gr/Shader.h>
+#include <anki/util/Hash.h>
 
 
 namespace anki
 namespace anki
 {
 {
@@ -106,11 +107,11 @@ enum class PipelineSubStateBit : U16
 };
 };
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(PipelineSubStateBit, inline)
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(PipelineSubStateBit, inline)
 
 
-/// Pipeline initializer.
-class PipelineInitInfo
+/// Only the state part of PipelineInitInfo. It's separate for easy hashing.
+class PipelineInitInfoState
 {
 {
 public:
 public:
-	PipelineInitInfo()
+	PipelineInitInfoState()
 	{
 	{
 // Do a special construction. The state will be hashed and the padding
 // Do a special construction. The state will be hashed and the padding
 // may contain garbage. With this trick zero the padding
 // may contain garbage. With this trick zero the padding
@@ -136,16 +137,39 @@ public:
 	RasterizerStateInfo m_rasterizer;
 	RasterizerStateInfo m_rasterizer;
 	DepthStencilStateInfo m_depthStencil;
 	DepthStencilStateInfo m_depthStencil;
 	ColorStateInfo m_color;
 	ColorStateInfo m_color;
+};
 
 
+/// Pipeline initializer.
+class PipelineInitInfo : public PipelineInitInfoState
+{
+public:
 	Array<ShaderPtr, 6> m_shaders;
 	Array<ShaderPtr, 6> m_shaders;
+
+	U64 computeHash() const
+	{
+		U64 h =
+			anki::computeHash(static_cast<const PipelineInitInfoState*>(this),
+				sizeof(PipelineInitInfoState));
+
+		Array<U64, m_shaders.getSize()> uuids;
+		for(U i = 0; i < m_shaders.getSize(); ++i)
+		{
+			U64 uuid = (m_shaders[i].isCreated()) ? m_shaders[i]->getUuid() : 0;
+			uuids[i] = uuid;
+		}
+
+		return appendHash(&uuids[0], sizeof(uuids), h);
+	}
 };
 };
 
 
 /// Graphics and compute pipeline. Contains the static state.
 /// Graphics and compute pipeline. Contains the static state.
 class Pipeline : public GrObject
 class Pipeline : public GrObject
 {
 {
 public:
 public:
+	static const GrObjectType CLASS_TYPE = GrObjectType::PIPELINE;
+
 	/// Construct.
 	/// Construct.
-	Pipeline(GrManager* manager);
+	Pipeline(GrManager* manager, U64 hash = 0);
 
 
 	/// Destroy.
 	/// Destroy.
 	~Pipeline();
 	~Pipeline();

+ 3 - 1
include/anki/gr/ResourceGroup.h

@@ -60,8 +60,10 @@ public:
 class ResourceGroup : public GrObject
 class ResourceGroup : public GrObject
 {
 {
 public:
 public:
+	static const GrObjectType CLASS_TYPE = GrObjectType::RESOURCE_GROUP;
+
 	/// Construct.
 	/// Construct.
-	ResourceGroup(GrManager* manager);
+	ResourceGroup(GrManager* manager, U64 hash = 0);
 
 
 	/// Destroy.
 	/// Destroy.
 	~ResourceGroup();
 	~ResourceGroup();

+ 3 - 1
include/anki/gr/Sampler.h

@@ -18,8 +18,10 @@ namespace anki
 class Sampler : public GrObject
 class Sampler : public GrObject
 {
 {
 public:
 public:
+	static const GrObjectType CLASS_TYPE = GrObjectType::SAMPLER;
+
 	/// Construct.
 	/// Construct.
-	Sampler(GrManager* manager);
+	Sampler(GrManager* manager, U64 hash = 0);
 
 
 	/// Destroy.
 	/// Destroy.
 	~Sampler();
 	~Sampler();

+ 3 - 1
include/anki/gr/Shader.h

@@ -63,8 +63,10 @@ void writeShaderBlockMemory(ShaderVariableDataType type,
 class Shader : public GrObject
 class Shader : public GrObject
 {
 {
 public:
 public:
+	static const GrObjectType CLASS_TYPE = GrObjectType::SHADER;
+
 	/// Construct.
 	/// Construct.
-	Shader(GrManager* manager);
+	Shader(GrManager* manager, U64 hash = 0);
 
 
 	/// Destroy.
 	/// Destroy.
 	~Shader();
 	~Shader();

+ 3 - 1
include/anki/gr/Texture.h

@@ -45,8 +45,10 @@ public:
 class Texture : public GrObject
 class Texture : public GrObject
 {
 {
 public:
 public:
+	static const GrObjectType CLASS_TYPE = GrObjectType::TEXTURE;
+
 	/// Construct.
 	/// Construct.
-	Texture(GrManager* manager);
+	Texture(GrManager* manager, U64 hash = 0);
 
 
 	/// Destroy.
 	/// Destroy.
 	~Texture();
 	~Texture();

+ 2 - 2
include/anki/gr/gl/GlState.h

@@ -46,12 +46,12 @@ public:
 
 
 	GLenum m_blendSfunc = GL_ONE;
 	GLenum m_blendSfunc = GL_ONE;
 	GLenum m_blendDfunc = GL_ZERO;
 	GLenum m_blendDfunc = GL_ZERO;
-
-	GLuint m_crntPpline = 0;
 	/// @}
 	/// @}
 
 
 	/// @name Pipeline state
 	/// @name Pipeline state
 	/// @{
 	/// @{
+	U64 m_lastPplineBoundUuid = MAX_U64;
+
 	Array<GLsizei, MAX_VERTEX_ATTRIBUTES> m_vertexBindingStrides;
 	Array<GLsizei, MAX_VERTEX_ATTRIBUTES> m_vertexBindingStrides;
 	GLenum m_topology = 0;
 	GLenum m_topology = 0;
 	U8 m_indexSize = 4;
 	U8 m_indexSize = 4;

+ 1 - 1
include/anki/gr/gl/ResourceGroupImpl.h

@@ -67,7 +67,7 @@ private:
 
 
 	/// Holds the references to the resources. Used to release the references
 	/// Holds the references to the resources. Used to release the references
 	/// gracefully
 	/// gracefully
-	DArray<IntrusivePtr<GrObject>> m_refs;
+	DArray<GrObjectPtr<GrObject>> m_refs;
 
 
 	template<typename InBindings, typename OutBindings>
 	template<typename InBindings, typename OutBindings>
 	void initBuffers(const InBindings& in,
 	void initBuffers(const InBindings& in,

+ 7 - 0
include/anki/util/Hash.h

@@ -21,6 +21,13 @@ namespace anki
 /// @return The hash.
 /// @return The hash.
 U64 computeHash(const void* buffer, U32 bufferSize, U64 seed = 123);
 U64 computeHash(const void* buffer, U32 bufferSize, U64 seed = 123);
 
 
+/// Computes a hash of a buffer.
+/// This function implements the MurmurHash2 algorithm by Austin Appleby.
+/// @param[in] buffer The buffer to hash.
+/// @param bufferSize The size of the buffer.
+/// @param prevHash The hash to append to.
+/// @return The new hash.
+U64 appendHash(const void* buffer, U32 bufferSize, U64 prevHash);
 /// @}
 /// @}
 
 
 } // end namespace anki
 } // end namespace anki

+ 1 - 1
include/anki/util/Thread.h

@@ -125,7 +125,7 @@ private:
 	void* m_impl = nullptr; ///< The system native type
 	void* m_impl = nullptr; ///< The system native type
 };
 };
 
 
-/// Mutual exclusion primitive. Like Mutex. It's better than Mutex only if the 
+/// Mutual exclusion primitive. Like Mutex. It's better than Mutex only if the
 /// critical section will be executed in a very short period of time.
 /// critical section will be executed in a very short period of time.
 class SpinLock : public NonCopyable
 class SpinLock : public NonCopyable
 {
 {

+ 1 - 1
include/anki/util/ThreadHive.h

@@ -46,7 +46,7 @@ public:
 	ThreadHiveDependencyHandle m_outDependency;
 	ThreadHiveDependencyHandle m_outDependency;
 };
 };
 
 
-/// A scheduler of small tasks. It takes a number of tasks and schedules them in 
+/// A scheduler of small tasks. It takes a number of tasks and schedules them in
 /// one of the threads. The tasks can depend on previously submitted tasks or be
 /// one of the threads. The tasks can depend on previously submitted tasks or be
 /// completely independent.
 /// completely independent.
 class ThreadHive : public NonCopyable
 class ThreadHive : public NonCopyable

+ 3 - 0
src/core/Trace.cpp

@@ -43,6 +43,9 @@ static Array<const char*, U(TraceCounterType::COUNT)> counterNames = {
 		"GR_DYNAMIC_UNIFORMS_SIZE",
 		"GR_DYNAMIC_UNIFORMS_SIZE",
 		"GR_DYNAMIC_STORAGE_SIZE",
 		"GR_DYNAMIC_STORAGE_SIZE",
 		"GR_VERTICES",
 		"GR_VERTICES",
+		"GR_PIPELINES_CREATED",
+		"GR_PIPELINE_BINDS_SKIPPED",
+		"GR_PIPELINE_BINDS_HAPPENED",
 		"RENDERER_LIGHTS",
 		"RENDERER_LIGHTS",
 		"RENDERER_SHADOW_PASSES",
 		"RENDERER_SHADOW_PASSES",
 		"RENDERER_MERGED_DRAWCALLS",
 		"RENDERER_MERGED_DRAWCALLS",

+ 9 - 1
src/gr/GrObject.cpp

@@ -10,13 +10,21 @@ namespace anki
 {
 {
 
 
 //==============================================================================
 //==============================================================================
-GrObject::GrObject(GrManager* manager)
+GrObject::GrObject(GrManager* manager, GrObjectType type, U64 hash)
 	: m_refcount(0)
 	: m_refcount(0)
 	, m_manager(manager)
 	, m_manager(manager)
 	, m_uuid(m_manager->getUuidIndex()++)
 	, m_uuid(m_manager->getUuidIndex()++)
+	, m_hash(hash)
+	, m_type(type)
 {
 {
 }
 }
 
 
+//==============================================================================
+GrObject::~GrObject()
+{
+	m_manager->unregisterCachedObject(this);
+}
+
 //==============================================================================
 //==============================================================================
 GrAllocator<U8> GrObject::getAllocator() const
 GrAllocator<U8> GrObject::getAllocator() const
 {
 {

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

@@ -11,8 +11,8 @@ namespace anki
 {
 {
 
 
 //==============================================================================
 //==============================================================================
-Buffer::Buffer(GrManager* manager)
-	: GrObject(manager)
+Buffer::Buffer(GrManager* manager, U64 hash)
+	: GrObject(manager, CLASS_TYPE, hash)
 {
 {
 }
 }
 
 

+ 13 - 9
src/gr/gl/CommandBuffer.cpp

@@ -26,8 +26,8 @@ namespace anki
 {
 {
 
 
 //==============================================================================
 //==============================================================================
-CommandBuffer::CommandBuffer(GrManager* manager)
-	: GrObject(manager)
+CommandBuffer::CommandBuffer(GrManager* manager, U64 hash)
+	: GrObject(manager, CLASS_TYPE, hash)
 {
 {
 }
 }
 
 
@@ -163,18 +163,22 @@ public:
 
 
 	Error operator()(GlState& state)
 	Error operator()(GlState& state)
 	{
 	{
-		ANKI_TRACE_START_EVENT(GL_BIND_PPLINE);
+		if(state.m_lastPplineBoundUuid != m_ppline->getUuid())
+		{
+			ANKI_TRACE_START_EVENT(GL_BIND_PPLINE);
 
 
-		PipelineImpl& impl = m_ppline->getImplementation();
+			PipelineImpl& impl = m_ppline->getImplementation();
+			impl.bind(state);
+			state.m_lastPplineBoundUuid = m_ppline->getUuid();
+			ANKI_TRACE_INC_COUNTER(GR_PIPELINE_BINDS_HAPPENED, 1);
 
 
-		auto name = impl.getGlName();
-		if(state.m_crntPpline != name)
+			ANKI_TRACE_STOP_EVENT(GL_BIND_PPLINE);
+		}
+		else
 		{
 		{
-			impl.bind(state);
-			state.m_crntPpline = name;
+			ANKI_TRACE_INC_COUNTER(GR_PIPELINE_BINDS_SKIPPED, 1);
 		}
 		}
 
 
-		ANKI_TRACE_STOP_EVENT(GL_BIND_PPLINE);
 		return ErrorCode::NONE;
 		return ErrorCode::NONE;
 	}
 	}
 };
 };

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

@@ -11,8 +11,8 @@ namespace anki
 {
 {
 
 
 //==============================================================================
 //==============================================================================
-Framebuffer::Framebuffer(GrManager* manager)
-	: GrObject(manager)
+Framebuffer::Framebuffer(GrManager* manager, U64 hash)
+	: GrObject(manager, CLASS_TYPE, hash)
 {
 {
 }
 }
 
 

+ 5 - 0
src/gr/gl/GrManager.cpp

@@ -29,6 +29,11 @@ Error GrManager::init(GrManagerInitInfo& init)
 	m_alloc =
 	m_alloc =
 		HeapAllocator<U8>(init.m_allocCallback, init.m_allocCallbackUserData);
 		HeapAllocator<U8>(init.m_allocCallback, init.m_allocCallbackUserData);
 
 
+	for(auto& c : m_caches)
+	{
+		c.init(m_alloc);
+	}
+
 	m_cacheDir.create(m_alloc, init.m_cacheDirectory);
 	m_cacheDir.create(m_alloc, init.m_cacheDirectory);
 	m_impl.reset(m_alloc.newInstance<GrManagerImpl>(this));
 	m_impl.reset(m_alloc.newInstance<GrManagerImpl>(this));
 	ANKI_CHECK(m_impl->init(init));
 	ANKI_CHECK(m_impl->init(init));

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

@@ -11,8 +11,8 @@ namespace anki
 {
 {
 
 
 //==============================================================================
 //==============================================================================
-OcclusionQuery::OcclusionQuery(GrManager* manager)
-	: GrObject(manager)
+OcclusionQuery::OcclusionQuery(GrManager* manager, U64 hash)
+	: GrObject(manager, CLASS_TYPE, hash)
 {
 {
 }
 }
 
 

+ 4 - 2
src/gr/gl/Pipeline.cpp

@@ -6,14 +6,16 @@
 #include <anki/gr/Pipeline.h>
 #include <anki/gr/Pipeline.h>
 #include <anki/gr/gl/PipelineImpl.h>
 #include <anki/gr/gl/PipelineImpl.h>
 #include <anki/gr/gl/CommandBufferImpl.h>
 #include <anki/gr/gl/CommandBufferImpl.h>
+#include <anki/core/Trace.h>
 
 
 namespace anki
 namespace anki
 {
 {
 
 
 //==============================================================================
 //==============================================================================
-Pipeline::Pipeline(GrManager* manager)
-	: GrObject(manager)
+Pipeline::Pipeline(GrManager* manager, U64 hash)
+	: GrObject(manager, CLASS_TYPE, hash)
 {
 {
+	ANKI_TRACE_INC_COUNTER(GR_PIPELINES_CREATED, 1);
 }
 }
 
 
 //==============================================================================
 //==============================================================================

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

@@ -14,8 +14,8 @@ namespace anki
 {
 {
 
 
 //==============================================================================
 //==============================================================================
-ResourceGroup::ResourceGroup(GrManager* manager)
-	: GrObject(manager)
+ResourceGroup::ResourceGroup(GrManager* manager, U64 hash)
+	: GrObject(manager, CLASS_TYPE, hash)
 {
 {
 }
 }
 
 

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

@@ -11,8 +11,8 @@ namespace anki
 {
 {
 
 
 //==============================================================================
 //==============================================================================
-Sampler::Sampler(GrManager* manager)
-	: GrObject(manager)
+Sampler::Sampler(GrManager* manager, U64 hash)
+	: GrObject(manager, CLASS_TYPE, hash)
 {
 {
 }
 }
 
 

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

@@ -11,8 +11,8 @@ namespace anki
 {
 {
 
 
 //==============================================================================
 //==============================================================================
-Shader::Shader(GrManager* manager)
-	: GrObject(manager)
+Shader::Shader(GrManager* manager, U64 hash)
+	: GrObject(manager, CLASS_TYPE, hash)
 {
 {
 }
 }
 
 

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

@@ -11,8 +11,8 @@ namespace anki
 {
 {
 
 
 //==============================================================================
 //==============================================================================
-Texture::Texture(GrManager* manager)
-	: GrObject(manager)
+Texture::Texture(GrManager* manager, U64 hash)
+	: GrObject(manager, CLASS_TYPE, hash)
 {
 {
 }
 }
 
 

+ 2 - 2
src/resource/Model.cpp

@@ -163,8 +163,8 @@ PipelinePtr ModelPatch::getPipeline(const RenderingKey& key) const
 			variant.getShader(ShaderType::FRAGMENT);
 			variant.getShader(ShaderType::FRAGMENT);
 
 
 		// Create
 		// Create
-		ppline = m_model->getManager().getGrManager().newInstance<Pipeline>(
-			pplineInit);
+		GrManager& gr = m_model->getManager().getGrManager();
+		ppline = gr.newInstanceCached<Pipeline>(pplineInit);
 	}
 	}
 
 
 	return ppline;
 	return ppline;

+ 19 - 14
src/util/Hash.cpp

@@ -8,14 +8,12 @@
 namespace anki
 namespace anki
 {
 {
 
 
+const U64 HASH_M = 0xc6a4a7935bd1e995;
+const U64 HASH_R = 47;
+
 //==============================================================================
 //==============================================================================
-U64 computeHash(const void* buffer, U32 bufferSize, U64 seed)
+U64 appendHash(const void* buffer, U32 bufferSize, U64 h)
 {
 {
-	const U64 m = 0xc6a4a7935bd1e995;
-	const U64 r = 47;
-
-	U64 h = seed ^ (bufferSize * m);
-
 	const U64* data = static_cast<const U64*>(buffer);
 	const U64* data = static_cast<const U64*>(buffer);
 	const U64* end = data + (bufferSize / sizeof(U64));
 	const U64* end = data + (bufferSize / sizeof(U64));
 
 
@@ -23,12 +21,12 @@ U64 computeHash(const void* buffer, U32 bufferSize, U64 seed)
 	{
 	{
 		U64 k = *data++;
 		U64 k = *data++;
 
 
-		k *= m;
-		k ^= k >> r;
-		k *= m;
+		k *= HASH_M;
+		k ^= k >> HASH_R;
+		k *= HASH_M;
 
 
 		h ^= k;
 		h ^= k;
-		h *= m;
+		h *= HASH_M;
 	}
 	}
 
 
 	const U8* data2 = reinterpret_cast<const U8*>(data);
 	const U8* data2 = reinterpret_cast<const U8*>(data);
@@ -49,14 +47,21 @@ U64 computeHash(const void* buffer, U32 bufferSize, U64 seed)
 		h ^= static_cast<U64>(data2[1]) << 8;
 		h ^= static_cast<U64>(data2[1]) << 8;
 	case 1:
 	case 1:
 		h ^= static_cast<U64>(data2[0]);
 		h ^= static_cast<U64>(data2[0]);
-		h *= m;
+		h *= HASH_M;
 	};
 	};
 
 
-	h ^= h >> r;
-	h *= m;
-	h ^= h >> r;
+	h ^= h >> HASH_R;
+	h *= HASH_M;
+	h ^= h >> HASH_R;
 
 
 	return h;
 	return h;
 }
 }
 
 
+//==============================================================================
+U64 computeHash(const void* buffer, U32 bufferSize, U64 seed)
+{
+	U64 h = seed ^ (bufferSize * HASH_M);
+	return appendHash(buffer, bufferSize, h);
+}
+
 } // end namespace anki
 } // end namespace anki