Bladeren bron

Some improvements in the stack memory pool

Panagiotis Christopoulos Charitos 4 jaren geleden
bovenliggende
commit
739d1f9fb0

+ 1 - 3
AnKi/Resource/ImageLoader.cpp

@@ -406,8 +406,6 @@ Error ImageLoader::loadAnkiTexture(FileInterface& file, U32 maxTextureSize,
 
 	if((header.m_compressionFormats & preferredCompression) == ImageLoaderDataCompression::NONE)
 	{
-		ANKI_RESOURCE_LOGW("File does not contain the requested compression");
-
 		// Fallback
 		preferredCompression = ImageLoaderDataCompression::RAW;
 
@@ -682,7 +680,7 @@ Error ImageLoader::loadInternal(FileInterface& file, const CString& filename, U3
 		ANKI_CHECK(loadAnkiTexture(file, maxTextureSize, m_compression, m_surfaces, m_volumes, m_alloc, m_width,
 								   m_height, m_depth, m_layerCount, m_mipCount, m_textureType, m_colorFormat));
 	}
-	else if(ext == "png")
+	else if(ext == "png" || ext == "jpg")
 	{
 		m_surfaces.create(m_alloc, 1);
 

+ 17 - 0
AnKi/Resource/ResourceFilesystem.cpp

@@ -427,6 +427,23 @@ Error ResourceFilesystem::openFile(const ResourceFilename& filename, ResourceFil
 		return err;
 	}
 
+	// File not found? Try to find it outside the resource dirs
+	if(!rfile && fileExists(filename))
+	{
+		CResourceFile* file = m_alloc.newInstance<CResourceFile>(m_alloc);
+		err = file->m_file.open(filename, FileOpenFlag::READ);
+		if(err)
+		{
+			m_alloc.deleteInstance(file);
+			return err;
+		}
+
+		rfile = file;
+		ANKI_RESOURCE_LOGW(
+			"Loading resource outside the resource paths/archives. This is only OK for tools and debugging: %s",
+			filename.cstr());
+	}
+
 	if(!rfile)
 	{
 		ANKI_RESOURCE_LOGE("File not found: %s", &filename[0]);

+ 1 - 1
AnKi/Resource/ResourceManager.cpp

@@ -45,7 +45,7 @@ Error ResourceManager::init(ResourceManagerInitInfo& init)
 	m_fs = init.m_resourceFs;
 	m_alloc = ResourceAllocator<U8>(init.m_allocCallback, init.m_allocCallbackData);
 
-	m_tmpAlloc = TempResourceAllocator<U8>(init.m_allocCallback, init.m_allocCallbackData, 10 * 1024 * 1024);
+	m_tmpAlloc = TempResourceAllocator<U8>(init.m_allocCallback, init.m_allocCallbackData, 10_MB);
 
 	m_cacheDir.create(m_alloc, init.m_cacheDir);
 

+ 83 - 103
AnKi/Util/Memory.cpp

@@ -261,7 +261,7 @@ StackMemoryPool::~StackMemoryPool()
 	}
 
 	// Do some error checks
-	auto allocCount = m_allocationsCount.load();
+	const U32 allocCount = m_allocationsCount.load();
 	if(!m_ignoreDeallocationErrors && allocCount != 0)
 	{
 		ANKI_UTIL_LOGW("Forgot to deallocate");
@@ -285,24 +285,6 @@ void StackMemoryPool::init(AllocAlignedCallback allocCb, void* allocCbUserData,
 	m_nextChunkScale = nextChunkScale;
 	m_nextChunkBias = nextChunkBias;
 	m_ignoreDeallocationErrors = ignoreDeallocationErrors;
-
-	// Create the first chunk
-	void* mem = m_allocCb(m_allocCbUserData, nullptr, m_initialChunkSize, m_alignmentBytes);
-
-	if(mem != nullptr)
-	{
-		invalidateMemory(mem, m_initialChunkSize);
-
-		m_chunks[0].m_baseMem = static_cast<U8*>(mem);
-		m_chunks[0].m_mem.store(m_chunks[0].m_baseMem);
-		m_chunks[0].m_size = initialChunkSize;
-
-		ANKI_ASSERT(m_crntChunkIdx.load() == 0);
-	}
-	else
-	{
-		ANKI_CREATION_OOM_ACTION();
-	}
 }
 
 void* StackMemoryPool::allocate(PtrSize size, PtrSize alignment)
@@ -313,26 +295,29 @@ void* StackMemoryPool::allocate(PtrSize size, PtrSize alignment)
 
 	size = getAlignedRoundUp(m_alignmentBytes, size);
 	ANKI_ASSERT(size > 0);
-	ANKI_ASSERT(size <= m_initialChunkSize && "The chunks should have enough space to hold at least one allocation");
 
-	Chunk* crntChunk = nullptr;
-	Bool retry = true;
 	U8* out = nullptr;
 
-	do
+	while(true)
 	{
-		crntChunk = &m_chunks[m_crntChunkIdx.load()];
-		crntChunk->check();
+		// Try to allocate from the current chunk, if there is one
+		Chunk* crntChunk = nullptr;
+		const I32 crntChunkIdx = m_crntChunkIdx.load();
+		if(crntChunkIdx >= 0)
+		{
+			crntChunk = &m_chunks[crntChunkIdx];
+			crntChunk->check();
 
-		out = crntChunk->m_mem.fetchAdd(size);
-		ANKI_ASSERT(out >= crntChunk->m_baseMem);
+			out = crntChunk->m_mem.fetchAdd(size);
+			ANKI_ASSERT(out >= crntChunk->m_baseMem);
+		}
 
-		if(PtrSize(out + size - crntChunk->m_baseMem) <= crntChunk->m_size)
+		if(crntChunk && out + size <= crntChunk->m_baseMem + crntChunk->m_size)
 		{
 			// All is fine, there is enough space in the chunk
 
-			retry = false;
 			m_allocationsCount.fetchAdd(1);
+			break;
 		}
 		else
 		{
@@ -341,59 +326,82 @@ void* StackMemoryPool::allocate(PtrSize size, PtrSize alignment)
 			LockGuard<Mutex> lock(m_lock);
 
 			// Make sure that only one thread will create a new chunk
-			if(&m_chunks[m_crntChunkIdx.load()] == crntChunk)
+			const Bool someOtherThreadCreateAChunkWhileIWasHoldingTheLock = m_crntChunkIdx.load() != crntChunkIdx;
+			if(someOtherThreadCreateAChunkWhileIWasHoldingTheLock)
+			{
+				continue;
+			}
+
+			// We can create a new chunk
+
+			ANKI_ASSERT(crntChunkIdx >= -1);
+			if(U32(crntChunkIdx + 1) >= m_chunks.getSize())
+			{
+				out = nullptr;
+				ANKI_UTIL_LOGE("Number of chunks is not enough");
+				ANKI_OOM_ACTION();
+				break;
+			}
+
+			// Compute the memory of the new chunk. Don't look at the previous chunk
+			PtrSize newChunkSize = m_initialChunkSize;
+			for(I i = 0; i < crntChunkIdx + 1; ++i)
+			{
+				newChunkSize = PtrSize(F64(newChunkSize) * m_nextChunkScale) + m_nextChunkBias;
+			}
+
+			newChunkSize = max(size, newChunkSize); // Can't have the allocation fail
+			alignRoundUp(m_alignmentBytes, newChunkSize); // Always align at the end
+
+			// Point to the next chunk
+			Chunk* newChunk = &m_chunks[crntChunkIdx + 1];
+
+			if(newChunk->m_baseMem == nullptr || newChunk->m_size != newChunkSize)
 			{
-				// We can create a new chunk
+				// Chunk is empty or its memory doesn't match the expected, need to (re)initialize it
 
-				PtrSize oldChunkSize = crntChunk->m_size;
-				++crntChunk;
-				if(crntChunk >= m_chunks.getEnd())
+				if(newChunk->m_baseMem)
 				{
-					ANKI_UTIL_LOGE("Number of chunks is not enough. Expect a crash");
+					m_allocCb(m_allocCbUserData, newChunk->m_baseMem, 0, 0);
+					m_allocatedMemory -= newChunk->m_size;
 				}
 
-				if(crntChunk->m_baseMem == nullptr)
+				void* mem = m_allocCb(m_allocCbUserData, nullptr, newChunkSize, m_alignmentBytes);
+
+				if(mem != nullptr)
 				{
-					// Need to create a new chunk
-
-					PtrSize newChunkSize = PtrSize(F32(oldChunkSize) * m_nextChunkScale) + m_nextChunkBias;
-					alignRoundUp(m_alignmentBytes, newChunkSize);
-
-					void* mem = m_allocCb(m_allocCbUserData, nullptr, newChunkSize, m_alignmentBytes);
-
-					if(mem != nullptr)
-					{
-						invalidateMemory(mem, newChunkSize);
-
-						crntChunk->m_baseMem = static_cast<U8*>(mem);
-						crntChunk->m_mem.store(crntChunk->m_baseMem);
-						crntChunk->m_size = newChunkSize;
-
-						U idx = m_crntChunkIdx.fetchAdd(1);
-						ANKI_ASSERT(&m_chunks[idx] == crntChunk - 1);
-						(void)idx;
-					}
-					else
-					{
-						out = nullptr;
-						retry = false;
-						ANKI_OOM_ACTION();
-					}
+					invalidateMemory(mem, newChunkSize);
+
+					newChunk->m_baseMem = static_cast<U8*>(mem);
+					newChunk->m_mem.setNonAtomically(newChunk->m_baseMem);
+					newChunk->m_size = newChunkSize;
+
+					m_allocatedMemory += newChunk->m_size;
+
+					const I32 idx = m_crntChunkIdx.fetchAdd(1);
+					ANKI_ASSERT(idx == crntChunkIdx);
+					(void)idx;
 				}
 				else
 				{
-					// Need to recycle one
+					out = nullptr;
+					ANKI_OOM_ACTION();
+					break;
+				}
+			}
+			else
+			{
+				// Will recycle
 
-					crntChunk->checkReset();
-					invalidateMemory(crntChunk->m_baseMem, crntChunk->m_size);
+				newChunk->checkReset();
+				invalidateMemory(newChunk->m_baseMem, newChunk->m_size);
 
-					U idx = m_crntChunkIdx.fetchAdd(1);
-					ANKI_ASSERT(&m_chunks[idx] == crntChunk - 1);
-					(void)idx;
-				}
+				const I32 idx = m_crntChunkIdx.fetchAdd(1);
+				ANKI_ASSERT(idx == crntChunkIdx);
+				(void)idx;
 			}
 		}
-	} while(retry);
+	}
 
 	return static_cast<void*>(out);
 }
@@ -407,11 +415,10 @@ void StackMemoryPool::free(void* ptr)
 		return;
 	}
 
-	// ptr shouldn't be null or not aligned. If not aligned it was not
-	// allocated by this class
+	// ptr shouldn't be null or not aligned. If not aligned it was not allocated by this class
 	ANKI_ASSERT(ptr != nullptr && isAligned(m_alignmentBytes, ptr));
 
-	auto count = m_allocationsCount.fetchSub(1);
+	const U32 count = m_allocationsCount.fetchSub(1);
 	ANKI_ASSERT(count > 0);
 	(void)count;
 }
@@ -437,29 +444,16 @@ void StackMemoryPool::reset()
 	}
 
 	// Set the crnt chunk
-	m_chunks[0].checkReset();
-	m_crntChunkIdx.store(0);
+	m_crntChunkIdx.setNonAtomically(-1);
 
 	// Reset allocation count and do some error checks
-	auto allocCount = m_allocationsCount.exchange(0);
+	const U32 allocCount = m_allocationsCount.exchange(0);
 	if(!m_ignoreDeallocationErrors && allocCount != 0)
 	{
 		ANKI_UTIL_LOGW("Forgot to deallocate");
 	}
 }
 
-PtrSize StackMemoryPool::getMemoryCapacity() const
-{
-	PtrSize sum = 0;
-	U crntChunkIdx = m_crntChunkIdx.load();
-	for(U i = 0; i <= crntChunkIdx; ++i)
-	{
-		sum += m_chunks[i].m_size;
-	}
-
-	return sum;
-}
-
 ChainMemoryPool::ChainMemoryPool()
 	: BaseMemoryPool(Type::CHAIN)
 {
@@ -479,13 +473,6 @@ ChainMemoryPool::~ChainMemoryPool()
 		destroyChunk(ch);
 		ch = next;
 	}
-
-	if(m_lock)
-	{
-		ANKI_ASSERT(m_allocCb);
-		m_lock->~SpinLock();
-		m_allocCb(m_allocCbUserData, m_lock, 0, 0);
-	}
 }
 
 void ChainMemoryPool::init(AllocAlignedCallback allocCb, void* allocCbUserData, PtrSize initialChunkSize,
@@ -505,13 +492,6 @@ void ChainMemoryPool::init(AllocAlignedCallback allocCb, void* allocCbUserData,
 	m_bias = nextChunkBias;
 	m_headerSize = max<PtrSize>(m_alignmentBytes, sizeof(Chunk*));
 
-	m_lock = reinterpret_cast<SpinLock*>(m_allocCb(m_allocCbUserData, nullptr, sizeof(SpinLock), alignof(SpinLock)));
-	if(!m_lock)
-	{
-		ANKI_CREATION_OOM_ACTION();
-	}
-	::new(m_lock) SpinLock();
-
 	// Initial size should be > 0
 	ANKI_ASSERT(m_initSize > 0 && "Wrong arg");
 
@@ -529,7 +509,7 @@ void* ChainMemoryPool::allocate(PtrSize size, PtrSize alignment)
 	Chunk* ch;
 	void* mem = nullptr;
 
-	LockGuard<SpinLock> lock(*m_lock);
+	LockGuard<SpinLock> lock(m_lock);
 
 	// Get chunk
 	ch = m_tailChunk;
@@ -576,7 +556,7 @@ void ChainMemoryPool::free(void* ptr)
 	ANKI_ASSERT(chunk != nullptr);
 	ANKI_ASSERT((mem >= chunk->m_memory && mem < (chunk->m_memory + chunk->m_memsize)) && "Wrong chunk");
 
-	LockGuard<SpinLock> lock(*m_lock);
+	LockGuard<SpinLock> lock(m_lock);
 
 	// Decrease the deallocation refcount and if it's zero delete the chunk
 	ANKI_ASSERT(chunk->m_allocationsCount > 0);

+ 29 - 23
AnKi/Util/Memory.h

@@ -16,9 +16,6 @@
 namespace anki
 {
 
-// Forward
-class SpinLock;
-
 /// @addtogroup util_memory
 /// @{
 
@@ -173,34 +170,41 @@ public:
 	/// Destroy
 	~StackMemoryPool();
 
-	/// Init with parameters
-	/// @param allocCb The allocation function callback
-	/// @param allocCbUserData The user data to pass to the allocation function
+	/// Init with parameters.
+	/// @param allocCb The allocation function callback.
+	/// @param allocCbUserData The user data to pass to the allocation function.
 	/// @param initialChunkSize The size of the first chunk.
 	/// @param nextChunkScale Value that controls the next chunk.
 	/// @param nextChunkBias Value that controls the next chunk.
 	/// @param ignoreDeallocationErrors Method free() may fail if the ptr is not in the top of the stack. Set that to
-	///        true to suppress such errors
-	/// @param alignmentBytes The maximum supported alignment for returned memory
+	///        true to suppress such errors.
+	/// @param alignmentBytes The maximum supported alignment for returned memory.
 	void init(AllocAlignedCallback allocCb, void* allocCbUserData, PtrSize initialChunkSize, F32 nextChunkScale = 2.0,
 			  PtrSize nextChunkBias = 0, Bool ignoreDeallocationErrors = true,
 			  PtrSize alignmentBytes = ANKI_SAFE_ALIGNMENT);
 
-	/// Allocate aligned memory. The operation is thread safe
-	/// @param size The size to allocate
-	/// @param alignmentBytes The alignment of the returned address
-	/// @return The allocated memory or nullptr on failure
+	/// Allocate aligned memory.
+	/// @param size The size to allocate.
+	/// @param alignmentBytes The alignment of the returned address.
+	/// @return The allocated memory or nullptr on failure.
+	///
+	/// @note The operation is thread safe with allocate() and free() methods.
 	void* allocate(PtrSize size, PtrSize alignmentBytes);
 
-	/// Free memory in StackMemoryPool. It will not actially free anything.
-	/// @param[in, out] ptr Memory block to deallocate
+	/// Free memory in StackMemoryPool. It will not actually do anything.
+	/// @param[in, out] ptr Memory block to deallocate.
 	void free(void* ptr);
 
-	/// Reinit the pool. All existing allocated memory will be lost
+	/// Reinit the pool. All existing allocated memory is effectively invalidated.
+	/// @note It's not thread safe with other methods.
 	void reset();
 
-	/// Get the current capacity of the pool. It's not thread safe.
-	PtrSize getMemoryCapacity() const;
+	/// Get the physical memory allocated by the pool.
+	/// @note It's not thread safe with other methods.
+	PtrSize getMemoryCapacity() const
+	{
+		return m_allocatedMemory;
+	}
 
 private:
 	/// The memory chunk.
@@ -245,12 +249,14 @@ private:
 	/// Chunk bias.
 	PtrSize m_nextChunkBias = 0;
 
+	/// Allocated memory.
+	PtrSize m_allocatedMemory = 0;
+
 	/// Ignore deallocation errors.
 	Bool m_ignoreDeallocationErrors = false;
 
-	/// The current chunk. Chose the more strict memory order to avoid compiler
-	/// re-ordering of instructions
-	Atomic<U32, AtomicMemoryOrder::SEQ_CST> m_crntChunkIdx = {0};
+	/// The current chunk. Chose the more strict memory order to avoid compiler re-ordering of instructions
+	Atomic<I32, AtomicMemoryOrder::SEQ_CST> m_crntChunkIdx = {-1};
 
 	/// The max number of chunks.
 	static const U MAX_CHUNKS = 256;
@@ -333,12 +339,12 @@ private:
 	/// Current chunk to allocate from.
 	Chunk* m_tailChunk = nullptr;
 
-	/// Fast thread locking.
-	SpinLock* m_lock = nullptr;
-
 	/// Size of the first chunk.
 	PtrSize m_initSize = 0;
 
+	/// Fast thread locking.
+	SpinLock m_lock;
+
 	/// Chunk scale.
 	F32 m_scale = 2.0;
 

+ 6 - 2
Tools/Texture/TextureViewerMain.cpp

@@ -54,6 +54,7 @@ private:
 	void draw(CanvasPtr& canvas)
 	{
 		const Texture& grTex = *m_textureResource->getGrTexture().get();
+		const U32 colorComponentCount = getFormatInfo(grTex.getFormat()).m_componentCount;
 
 		ImGui::Begin("Console", nullptr,
 					 ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove);
@@ -94,8 +95,11 @@ private:
 		ImGui::SameLine();
 		ImGui::Checkbox("Blue", &m_colorChannel[2]);
 		ImGui::SameLine();
-		ImGui::Checkbox("Alpha", &m_colorChannel[3]);
-		ImGui::SameLine();
+		if(colorComponentCount == 4)
+		{
+			ImGui::Checkbox("Alpha", &m_colorChannel[3]);
+			ImGui::SameLine();
+		}
 		ImGui::Spacing();
 		ImGui::SameLine();