Răsfoiți Sursa

More work on the transient allocator

Panagiotis Christopoulos Charitos 8 ani în urmă
părinte
comite
64a43d1049

+ 2 - 2
samples/common/Framework.cpp

@@ -12,7 +12,7 @@ Error SampleApp::init(int argc, char** argv)
 	// Init the super class
 	Config config;
 	config.set("fullscreenDesktopResolution", true);
-	config.set("dataPaths", ".:../..");
+	config.set("rsrc.dataPaths", ".:../..");
 	config.set("debugContext", 0);
 	ANKI_CHECK(config.setFromCommandLineArguments(argc, argv));
 	ANKI_CHECK(App::init(config, allocAligned, nullptr));
@@ -137,4 +137,4 @@ Error SampleApp::userMainLoop(Bool& quit)
 	}
 
 	return ErrorCode::NONE;
-}
+}

+ 3 - 3
sandbox/config.xml

@@ -43,9 +43,9 @@
 	<core.storagePerFrameMemorySize>16777216</core.storagePerFrameMemorySize>
 	<core.transferPerFrameMemorySize>67108864</core.transferPerFrameMemorySize>
 	<core.vertexPerFrameMemorySize>16777216</core.vertexPerFrameMemorySize>
-	<maxTextureSize>1048576</maxTextureSize>
-	<textureAnisotropy>8</textureAnisotropy>
-	<dataPaths>assets:.</dataPaths>
+	<rsrc.maxTextureSize>1048576</rsrc.maxTextureSize>
+	<rsrc.textureAnisotropy>8</rsrc.textureAnisotropy>
+	<rsrc.dataPaths>assets:.</rsrc.dataPaths>
 	<glmajor>4</glmajor>
 	<glminor>5</glminor>
 	<fullscreenDesktopResolution>1</fullscreenDesktopResolution>

+ 1 - 1
src/anki/core/App.cpp

@@ -265,7 +265,7 @@ Error App::initInternal(const ConfigSet& config_, AllocAlignedCallback allocCb,
 	rinit.m_allocCallbackData = m_allocCbData;
 	m_resources = m_heapAlloc.newInstance<ResourceManager>();
 
-	ANKI_CHECK(m_resources->create(rinit));
+	ANKI_CHECK(m_resources->init(rinit));
 
 	//
 	// Renderer

+ 10 - 9
src/anki/core/Config.cpp

@@ -68,9 +68,10 @@ Config::Config()
 	newOption("clearCaches", false);
 
 	// Resource
-	newOption("maxTextureSize", 1024 * 1024);
-	newOption("textureAnisotropy", 8);
-	newOption("dataPaths", ".");
+	newOption("rsrc.maxTextureSize", 1024 * 1024);
+	newOption("rsrc.textureAnisotropy", 8);
+	newOption("rsrc.dataPaths", ".");
+	newOption("rsrc.transferScratchMemorySize", 256_MB);
 
 	// Window
 	newOption("glmajor", 4);
@@ -81,14 +82,14 @@ Config::Config()
 	newOption("debugMarkers", false);
 
 	// GR
-	newOption("gr.diskShaderCacheMaxSize", 10 * 1024 * 1024);
+	newOption("gr.diskShaderCacheMaxSize", 10_MB);
 
 	// Core
-	newOption("core.uniformPerFrameMemorySize", 1024 * 1024 * 16);
-	newOption("core.storagePerFrameMemorySize", 1024 * 1024 * 16);
-	newOption("core.vertexPerFrameMemorySize", 1024 * 1024 * 10);
-	newOption("core.transferPerFrameMemorySize", 1024 * 1024 * 128);
-	newOption("core.textureBufferPerFrameMemorySize", 1024 * 1024 * 1);
+	newOption("core.uniformPerFrameMemorySize", 16_MB);
+	newOption("core.storagePerFrameMemorySize", 16_MB);
+	newOption("core.vertexPerFrameMemorySize", 10_MB);
+	newOption("core.transferPerFrameMemorySize", 128_MB);
+	newOption("core.textureBufferPerFrameMemorySize", 1_MB);
 }
 
 Config::~Config()

+ 2 - 2
src/anki/resource/ResourceFilesystem.cpp

@@ -201,11 +201,11 @@ ResourceFilesystem::~ResourceFilesystem()
 Error ResourceFilesystem::init(const ConfigSet& config, const CString& cacheDir)
 {
 	StringListAuto paths(m_alloc);
-	paths.splitString(config.getString("dataPaths"), ':');
+	paths.splitString(config.getString("rsrc.dataPaths"), ':');
 
 	if(paths.getSize() < 1)
 	{
-		ANKI_RESOURCE_LOGE("Config option \"dataPaths\" is empty");
+		ANKI_RESOURCE_LOGE("Config option \"rsrc.dataPaths\" is empty");
 		return ErrorCode::USER_DATA;
 	}
 

+ 6 - 3
src/anki/resource/ResourceManager.cpp

@@ -32,9 +32,10 @@ ResourceManager::~ResourceManager()
 	m_cacheDir.destroy(m_alloc);
 	m_shadersPrependedSource.destroy(m_alloc);
 	m_alloc.deleteInstance(m_asyncLoader);
+	m_transferGpuAlloc.destroy();
 }
 
-Error ResourceManager::create(ResourceManagerInitInfo& init)
+Error ResourceManager::init(ResourceManagerInitInfo& init)
 {
 	m_gr = init.m_gr;
 	m_stagingMem = init.m_stagingMem;
@@ -48,8 +49,8 @@ Error ResourceManager::create(ResourceManagerInitInfo& init)
 
 	// Init some constants
 	//
-	m_maxTextureSize = init.m_config->getNumber("maxTextureSize");
-	m_textureAnisotropy = init.m_config->getNumber("textureAnisotropy");
+	m_maxTextureSize = init.m_config->getNumber("rsrc.maxTextureSize");
+	m_textureAnisotropy = init.m_config->getNumber("rsrc.textureAnisotropy");
 
 // Init type resource managers
 //
@@ -76,6 +77,8 @@ Error ResourceManager::create(ResourceManagerInitInfo& init)
 	m_asyncLoader = m_alloc.newInstance<AsyncLoader>();
 	m_asyncLoader->init(m_alloc);
 
+	ANKI_CHECK(m_transferGpuAlloc.init(init.m_config->getNumber("rsrc.transferScratchMemorySize"), m_gr, m_alloc));
+
 	return ErrorCode::NONE;
 }
 

+ 8 - 2
src/anki/resource/ResourceManager.h

@@ -5,7 +5,7 @@
 
 #pragma once
 
-#include <anki/resource/Common.h>
+#include <anki/resource/TransferGpuAllocator.h>
 #include <anki/util/List.h>
 #include <anki/util/Functions.h>
 #include <anki/util/String.h>
@@ -126,7 +126,7 @@ public:
 
 	~ResourceManager();
 
-	ANKI_USE_RESULT Error create(ResourceManagerInitInfo& init);
+	ANKI_USE_RESULT Error init(ResourceManagerInitInfo& init);
 
 	/// Load a resource.
 	template<typename T>
@@ -169,6 +169,11 @@ anki_internal:
 		return *m_stagingMem;
 	}
 
+	TransferGpuAllocator& getTransferGpuAllocator()
+	{
+		return m_transferGpuAlloc;
+	}
+
 	PhysicsWorld& getPhysicsWorld()
 	{
 		ANKI_ASSERT(m_physics);
@@ -243,6 +248,7 @@ private:
 	AsyncLoader* m_asyncLoader = nullptr; ///< Async loading thread
 	U64 m_uuid = 0;
 	U64 m_loadRequestCount = 0;
+	TransferGpuAllocator m_transferGpuAlloc;
 };
 /// @}
 

+ 145 - 29
src/anki/resource/TransferGpuAllocator.cpp

@@ -11,65 +11,181 @@
 namespace anki
 {
 
-class TransferGpuAllocator::Memory : public ClassGpuAllocatorMemory
+class TransferGpuAllocator::Memory : public StackGpuAllocatorMemory
 {
 public:
 	BufferPtr m_buffer;
 	void* m_mappedMemory;
-
-	DynamicArray<FencePtr> m_fences;
-	U32 m_fencesCount = 0;
 };
 
-class TransferGpuAllocator::ClassInf
-{
-public:
-	PtrSize m_slotSize;
-	PtrSize m_chunkSize;
-};
-
-static const Array<TransferGpuAllocator::ClassInf, 3> CLASSES = {{{8_MB, 128_MB}, {64_MB, 256_MB}, {128_MB, 256_MB}}};
-
-class TransferGpuAllocator::Interface : public ClassGpuAllocatorInterface
+class TransferGpuAllocator::Interface : public StackGpuAllocatorInterface
 {
 public:
 	GrManager* m_gr;
 	ResourceAllocator<U8> m_alloc;
 
-	Error allocate(U classIdx, ClassGpuAllocatorMemory*& mem) override
+	ResourceAllocator<U8> getAllocator() const
+	{
+		return m_alloc;
+	}
+
+	ANKI_USE_RESULT Error allocate(PtrSize size, StackGpuAllocatorMemory*& mem) final
 	{
 		TransferGpuAllocator::Memory* mm = m_alloc.newInstance<TransferGpuAllocator::Memory>();
 
-		const PtrSize size = CLASSES[classIdx].m_chunkSize;
 		mm->m_buffer = m_gr->newInstance<Buffer>(size, BufferUsageBit::BUFFER_UPLOAD_SOURCE, BufferMapAccessBit::WRITE);
-
-		// TODO
+		mm->m_mappedMemory = mm->m_buffer->map(0, size, BufferMapAccessBit::WRITE);
 
 		mem = mm;
-
 		return ErrorCode::NONE;
 	}
 
-	void free(ClassGpuAllocatorMemory* mem) override
+	void free(StackGpuAllocatorMemory* mem) final
 	{
 		ANKI_ASSERT(mem);
 
 		TransferGpuAllocator::Memory* mm = static_cast<TransferGpuAllocator::Memory*>(mem);
-		mm->m_fences.destroy(m_alloc);
-		mm->m_buffer.reset(nullptr);
-		mm->m_fencesCount = 0;
+		m_alloc.deleteInstance(mm);
 	}
 
-	U getClassCount() const override
+	void getChunkGrowInfo(F32& scale, PtrSize& bias, PtrSize& initialSize) final
 	{
-		return CLASSES.getSize();
+		scale = 1.5;
+		bias = 0;
+		initialSize = TransferGpuAllocator::CHUNK_INITIAL_SIZE;
 	}
 
-	void getClassInfo(U classIdx, PtrSize& slotSize, PtrSize& chunkSize) const override
+	U32 getMaxAlignment() final
 	{
-		slotSize = CLASSES[classIdx].m_slotSize;
-		chunkSize = CLASSES[classIdx].m_chunkSize;
+		return 16;
 	}
 };
 
-} // end namespace anki
+BufferPtr TransferGpuAllocatorHandle::getBuffer() const
+{
+	ANKI_ASSERT(m_handle.m_memory);
+	const TransferGpuAllocator::Memory* mm = static_cast<const TransferGpuAllocator::Memory*>(m_handle.m_memory);
+	ANKI_ASSERT(mm->m_buffer);
+	return mm->m_buffer;
+}
+
+void* TransferGpuAllocatorHandle::getMappedMemory() const
+{
+	ANKI_ASSERT(m_handle.m_memory);
+	const TransferGpuAllocator::Memory* mm = static_cast<const TransferGpuAllocator::Memory*>(m_handle.m_memory);
+	ANKI_ASSERT(mm->m_mappedMemory);
+	return mm->m_mappedMemory;
+}
+
+TransferGpuAllocator::TransferGpuAllocator()
+{
+}
+
+TransferGpuAllocator::~TransferGpuAllocator()
+{
+}
+
+Error TransferGpuAllocator::init(PtrSize maxSize, GrManager* gr, ResourceAllocator<U8> alloc)
+{
+	m_alloc = alloc;
+	m_gr = gr;
+
+	m_maxAllocSize = getAlignedRoundUp(CHUNK_INITIAL_SIZE * FRAME_COUNT, maxSize);
+	ANKI_RESOURCE_LOGI("Will use %uMB of memory for transfer scratch", m_maxAllocSize / 1024 / 1024);
+
+	m_interface.reset(m_alloc.newInstance<Interface>());
+	m_interface->m_gr = gr;
+	m_interface->m_alloc = alloc;
+
+	for(Frame& frame : m_frames)
+	{
+		frame.m_stackAlloc.init(m_alloc, m_interface.get());
+	}
+
+	return ErrorCode::NONE;
+}
+
+void TransferGpuAllocator::destroy()
+{
+	LockGuard<Mutex> lock(m_mtx);
+
+	for(Frame& frame : m_frames)
+	{
+		ANKI_ASSERT(frame.m_pendingReleases == 0);
+		frame.m_fences.destroy(m_alloc);
+	}
+}
+
+Error TransferGpuAllocator::allocate(PtrSize size, TransferGpuAllocatorHandle& handle)
+{
+	const PtrSize frameSize = m_maxAllocSize / FRAME_COUNT;
+
+	LockGuard<Mutex> lock(m_mtx);
+
+	Frame* frame;
+	if(m_crntFrameAllocatedSize + size <= frameSize)
+	{
+		// Have enough space in the frame
+
+		frame = &m_frames[m_frameCount];
+	}
+	else
+	{
+		// Don't have enough space. Wait for next frame
+
+		m_frameCount = (m_frameCount + 1) % FRAME_COUNT;
+		Frame& nextFrame = m_frames[m_frameCount];
+
+		// Wait for all memory to be released
+		while(nextFrame.m_pendingReleases != 0)
+		{
+			m_condVar.wait(m_mtx);
+		}
+
+		// Wait all fences
+		while(!nextFrame.m_fences.isEmpty())
+		{
+			FencePtr fence = nextFrame.m_fences.getFront();
+
+			const Bool done = fence->clientWait(MAX_FENCE_WAIT_TIME);
+			if(done)
+			{
+				nextFrame.m_fences.popFront(m_alloc);
+			}
+		}
+
+		nextFrame.m_stackAlloc.reset();
+		m_crntFrameAllocatedSize = 0;
+		frame = &nextFrame;
+	}
+
+	ANKI_CHECK(frame->m_stackAlloc.allocate(size, handle.m_handle));
+	handle.m_range = size;
+	handle.m_frame = frame - &m_frames[0];
+	m_crntFrameAllocatedSize += size;
+
+	return ErrorCode::NONE;
+}
+
+void TransferGpuAllocator::release(TransferGpuAllocatorHandle& handle, FencePtr fence)
+{
+	ANKI_ASSERT(fence);
+	ANKI_ASSERT(handle.valid());
+
+	Frame& frame = m_frames[handle.m_frame];
+
+	{
+		LockGuard<Mutex> lock(m_mtx);
+
+		frame.m_fences.pushBack(m_alloc, fence);
+
+		ANKI_ASSERT(frame.m_pendingReleases > 0);
+		--frame.m_pendingReleases;
+
+		m_condVar.notifyOne();
+	}
+
+	handle = {};
+}
+
+} // end namespace anki

+ 50 - 8
src/anki/resource/TransferGpuAllocator.h

@@ -6,7 +6,8 @@
 #pragma once
 
 #include <anki/resource/Common.h>
-#include <anki/gr/common/ClassGpuAllocator.h>
+#include <anki/gr/common/StackGpuAllocator.h>
+#include <anki/util/List.h>
 
 namespace anki
 {
@@ -17,6 +18,8 @@ namespace anki
 /// Memory handle.
 class TransferGpuAllocatorHandle
 {
+	friend class TransferGpuAllocator;
+
 public:
 	TransferGpuAllocatorHandle() = default;
 
@@ -27,7 +30,10 @@ public:
 		*this = std::move(b);
 	}
 
-	~TransferGpuAllocatorHandle();
+	~TransferGpuAllocatorHandle()
+	{
+		ANKI_ASSERT(!valid() && "Forgot to release");
+	}
 
 	TransferGpuAllocatorHandle& operator=(TransferGpuAllocatorHandle&& b)
 	{
@@ -38,6 +44,8 @@ public:
 
 	BufferPtr getBuffer() const;
 
+	void* getMappedMemory() const;
+
 	PtrSize getOffset() const
 	{
 		ANKI_ASSERT(m_handle);
@@ -52,29 +60,63 @@ public:
 	}
 
 private:
-	ClassGpuAllocatorHandle m_handle;
+	StackGpuAllocatorHandle m_handle;
 	PtrSize m_range = 0;
+	U8 m_frame = MAX_U8;
+
+	Bool valid() const
+	{
+		return m_range != 0 && m_frame < MAX_U8;
+	}
 };
 
 /// GPU memory allocator for GPU buffers used in transfer operations.
 class TransferGpuAllocator
 {
+	friend class TransferGpuAllocatorHandle;
+
 public:
-	class ClassInf;
+	static const U FRAME_COUNT = 3;
+	static const PtrSize CHUNK_INITIAL_SIZE = 64_MB;
+	static constexpr F64 MAX_FENCE_WAIT_TIME = 500.0_ms;
 
-	ANKI_USE_RESULT Bool allocate(PtrSize size, TransferGpuAllocatorHandle& handle);
+	TransferGpuAllocator();
 
+	~TransferGpuAllocator();
+
+	ANKI_USE_RESULT Error init(PtrSize maxSize, GrManager* gr, ResourceAllocator<U8> alloc);
+
+	void destroy();
+
+	/// Allocate some transfer memory.
+	ANKI_USE_RESULT Error allocate(PtrSize size, TransferGpuAllocatorHandle& handle);
+
+	/// Release the memory. It will not be recycled before the fence is signaled.
 	void release(TransferGpuAllocatorHandle& handle, FencePtr fence);
 
 private:
-	class Memory;
 	class Interface;
+	class Memory;
 
 	ResourceAllocator<U8> m_alloc;
 	GrManager* m_gr = nullptr;
+	PtrSize m_maxAllocSize = 0;
 
-	ClassGpuAllocatorInterface* m_interface = nullptr;
-	ClassGpuAllocator m_classAlloc;
+	UniquePtr<Interface> m_interface;
+
+	class Frame
+	{
+	public:
+		StackGpuAllocator m_stackAlloc;
+		List<FencePtr> m_fences;
+		U32 m_pendingReleases = 0;
+	};
+
+	Mutex m_mtx; ///< Protect all members bellow.
+	ConditionVariable m_condVar;
+	Array<Frame, FRAME_COUNT> m_frames;
+	U8 m_frameCount = 0;
+	PtrSize m_crntFrameAllocatedSize;
 };
 /// @}
 

+ 17 - 0
src/anki/util/StdTypes.h

@@ -185,6 +185,8 @@ private:
 #define ANKI_DBG_NULLIFY
 #endif
 
+/// @name Size user literals
+/// @{
 static constexpr unsigned long long int operator""_B(unsigned long long int x)
 {
 	return x;
@@ -201,4 +203,19 @@ static constexpr unsigned long long int operator""_MB(unsigned long long int x)
 }
 /// @}
 
+/// @name Time user literals
+/// @{
+static constexpr long double operator""_ms(long double x)
+{
+	return x / 1000.0;
+}
+
+static constexpr long double operator""_ns(long double x)
+{
+	return x / 1000000000.0;
+}
+/// @}
+
+/// @}
+
 } // end namespace anki

+ 1 - 1
tests/resource/ResourceManager.cpp

@@ -25,7 +25,7 @@ ANKI_TEST(Resource, ResourceManager)
 	rinit.m_allocCallback = allocAligned;
 	rinit.m_allocCallbackData = nullptr;
 	ResourceManager* resources = alloc.newInstance<ResourceManager>();
-	ANKI_TEST_EXPECT_NO_ERR(resources->create(rinit));
+	ANKI_TEST_EXPECT_NO_ERR(resources->init(rinit));
 
 	// Very simple
 	{