Forráskód Böngészése

added free list allocator and refactored imgui

vlod 3 éve
szülő
commit
8d671b98da

+ 0 - 1
Pika/CMakeLists.txt

@@ -31,7 +31,6 @@ add_subdirectory(thirdparty/stb_truetype)
 add_subdirectory(thirdparty/imgui-docking)
 
 
-
 #Define some macros for the project sources
 file(GLOB_RECURSE PIKA_SOURCES_CORE_CONFIG	CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/core/coreConfig/*.cpp")
 file(GLOB_RECURSE PIKA_SOURCES_CORE_EDITOR	CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/core/pikaEditor/*.cpp")

+ 532 - 0
Pika/core/pikaSTD/pikaAllocator/freeListAllocator.cpp

@@ -0,0 +1,532 @@
+//////////////////////////////////////////////////
+//freeListAllocator.h				beta 0.2
+//Copyright(c) 2020 Luta Vlad
+//https://github.com/meemknight/freeListAllocator
+//////////////////////////////////////////////////
+
+
+#include "freeListAllocator.h"
+#include <cstdint>
+#include <algorithm>
+
+
+#include <assert/assert.h>
+
+
+namespace pika
+{
+
+namespace allocator
+{
+
+	const uint64_t GUARD_VALUE = 0xff'ff'ff'ff'ff'ff'ff'ff;
+
+	struct FreeBlock
+		{
+			union
+			{
+				char *next;
+				std::uint64_t dummy_;
+			};
+			std::uint64_t size;
+
+		};
+
+	struct AllocatedBlock
+		{
+			std::uint64_t size;
+			std::uint64_t guard;
+		};
+
+
+	void FreeListAllocator::init(void *baseMemory, size_t memorySize)
+		{
+			end = (void *)((size_t)baseMemory + memorySize);
+
+			static_assert(sizeof(FreeBlock) == sizeof(AllocatedBlock), "");
+
+			PIKA_PERMA_ASSERT(memorySize > 100, "Memory size must be greater than 100 bytes");
+
+			this->baseMemory = (char *)baseMemory;
+
+			//move base memory to a 8 byte aligned
+
+			size_t pos = (int)this->baseMemory;
+
+			if (pos % 8 != 0)
+			{
+				this->baseMemory += 8 - (pos % 8);
+				memorySize -= 8 - (pos % 8);
+			}
+
+			((FreeBlock *)this->baseMemory)->next = nullptr;
+			((FreeBlock *)this->baseMemory)->size = memorySize - sizeof(FreeBlock);
+
+
+		}
+
+	void *FreeListAllocator::allocate(size_t size)
+		{
+
+			PIKA_PERMA_ASSERT(baseMemory, "Allocator not initialized"); //err allocator not initialized
+
+
+
+			FreeBlock *last = nullptr;
+			FreeBlock *current = (FreeBlock *)baseMemory;
+
+			int aligned8Size = size;
+			if (aligned8Size % 8 != 0)
+			{
+				aligned8Size += (8 - (aligned8Size % 8));
+			}
+
+			while (true)
+			{
+
+
+				if (aligned8Size <= ((FreeBlock *)current)->size) // this is a suitable block
+				{
+					//add block here
+
+
+					FreeBlock *next = (FreeBlock *)current->next;
+
+					if (next == nullptr || next >= this->end) //this is the last block
+					{
+						if (last == nullptr || last >= this->end) //this is also the first block so move the base pointer
+						{
+							void *toReturn = (char *)current + sizeof(AllocatedBlock);
+							((AllocatedBlock *)current)->size = aligned8Size;	//size of the new allocated block
+							((AllocatedBlock *)current)->guard = GUARD_VALUE;
+
+							FreeBlock *nextFreeBlock = (FreeBlock *)((char *)toReturn + aligned8Size);
+							//next free block is the base memory now
+
+							baseMemory = (char *)nextFreeBlock;
+							((FreeBlock *)baseMemory)->next = nullptr; //this is the last block
+
+							size_t size = this->getEnd() - (size_t)nextFreeBlock - (size_t)sizeof(FreeBlock); //set the size of the new last block
+							((FreeBlock *)baseMemory)->size = size;
+
+							return toReturn;
+						}
+						else //this is not the first block so change the last block pointer
+						{
+							void *toReturn = (char *)current + sizeof(AllocatedBlock);
+							((AllocatedBlock *)current)->size = aligned8Size;	//size of the new allcoated block
+							((AllocatedBlock *)current)->guard = GUARD_VALUE;
+
+							FreeBlock *nextFreeBlock = (FreeBlock *)((char *)toReturn + aligned8Size);
+
+							last->next = (char *)nextFreeBlock; //last is relinked
+							nextFreeBlock->next = nullptr; //this is the last block
+
+							size_t size = this->getEnd() - (size_t)nextFreeBlock - (size_t)sizeof(FreeBlock); //set the size of the new last block
+							nextFreeBlock->size = size;
+
+							return toReturn;
+						}
+
+					}
+					else //this is not the last free block 
+					{
+
+						if (last == nullptr || last >= this->end) // this is the first free block but not the last 
+						{
+							size_t currentSize = ((FreeBlock *)current)->size;
+
+							void *toReturn = (char *)current + sizeof(AllocatedBlock);
+
+							if (currentSize - aligned8Size < 24)
+							{
+								//too small block remaining
+
+								if (currentSize - aligned8Size < 0 || (currentSize - aligned8Size) % 8 != 0)
+								{
+									//heap corrupted
+									PIKA_PERMA_ASSERT(0, "heap corupted");
+								}
+
+								aligned8Size += (currentSize - aligned8Size);
+
+								((AllocatedBlock *)current)->size = aligned8Size;	//size of the new allocated block
+								((AllocatedBlock *)current)->guard = GUARD_VALUE;
+
+								FreeBlock *nextFreeBlock = next;
+								//next free block is the next block
+
+								baseMemory = (char *)nextFreeBlock;
+
+								return toReturn;
+
+							}
+							else
+							{
+								//add a new block
+								FreeBlock *newCreatedBlock = (FreeBlock *)((char *)toReturn + aligned8Size);
+								newCreatedBlock->size = currentSize - aligned8Size - sizeof(AllocatedBlock);
+								newCreatedBlock->next = (char *)next;
+
+								((AllocatedBlock *)current)->size = aligned8Size;	//size of the new allocated block
+								((AllocatedBlock *)current)->guard = GUARD_VALUE;
+								baseMemory = (char *)newCreatedBlock;
+
+								return toReturn;
+							}
+
+
+
+						}
+						else // this is not the first free block and not the last 
+						{	//todo fix here
+
+							void *toReturn = (char *)current + sizeof(AllocatedBlock);
+							size_t currentSize = ((FreeBlock *)current)->size;
+
+							if (currentSize - aligned8Size < 24)
+							{
+
+								//too small block remaining
+								if (currentSize - aligned8Size < 0 || (currentSize - aligned8Size) % 8 != 0)
+								{
+									//heap corrupted
+									PIKA_PERMA_ASSERT(0, "heap corupted");
+								}
+
+								aligned8Size += (currentSize - aligned8Size);
+
+								((AllocatedBlock *)current)->size = aligned8Size;	//size of the new allcoated block
+								((AllocatedBlock *)current)->guard = GUARD_VALUE;
+
+								//FreeBlock* nextFreeBlock = (FreeBlock*)((char*)toReturn + aligned8Size);
+
+								last->next = (char *)next; //last is relinked
+								//nextFreeBlock->next = (char*)next; //this is not the last block
+
+								//size_t size = this->getEnd() - (size_t)nextFreeBlock - (size_t)sizeof(FreeBlock); //set the size of the new last block
+								//nextFreeBlock->size = size;
+
+								return toReturn;
+
+							}
+							else
+							{
+								//add a new block
+								FreeBlock *newCreatedBlock = (FreeBlock *)((char *)toReturn + aligned8Size);
+								newCreatedBlock->size = currentSize - aligned8Size - sizeof(AllocatedBlock);
+								newCreatedBlock->next = (char *)next;
+
+								((AllocatedBlock *)current)->size = aligned8Size;	//size of the new allcoated block
+								((AllocatedBlock *)current)->guard = GUARD_VALUE;
+
+								//FreeBlock* nextFreeBlock = (FreeBlock*)((char*)toReturn + aligned8Size);
+
+								last->next = (char *)newCreatedBlock; //last is relinked
+								//nextFreeBlock->next = (char*)next; //this is not the last block
+
+								//size_t size = this->getEnd() - (size_t)nextFreeBlock - (size_t)sizeof(FreeBlock); //set the size of the new last block
+								//nextFreeBlock->size = size;
+
+								return toReturn;
+
+							}
+
+
+						}
+					}
+
+				}
+				else
+				{
+					if (current->next == nullptr || current->next >= this->end)
+					{
+						//that was the last block, no size
+						//std::cout << "no more memory\n";
+
+						if (returnZeroIfNoMoreMemory)
+						{
+							return 0;
+						}
+						else
+						{
+							PIKA_PERMA_ASSERT(0, "Allocator out of memory");
+						}
+
+					}
+					else
+					{
+						last = current;
+						current = (FreeBlock *)current->next;
+
+					}
+
+				}
+
+
+			}
+
+			PIKA_PERMA_ASSERT(0, "unreachable code");
+			return nullptr;
+		}
+
+	void FreeListAllocator::free(void *mem)
+		{
+			if (mem == nullptr) { return; }
+
+			char *memoryBegin = (char *)mem;
+
+			//mem is the beginning of the valid memory, \
+			the header is then 16 bytes lower
+
+			void *headerBegin = memoryBegin - sizeof(AllocatedBlock);
+
+			AllocatedBlock *allocatedBLockHeader = (AllocatedBlock *)headerBegin;
+
+		#pragma region check validity
+
+			PIKA_PERMA_ASSERT(allocatedBLockHeader->guard == GUARD_VALUE, "invalid free or double free"); //invalid free or double free
+
+		#pragma endregion
+
+			size_t sizeOfTheFreedBlock = allocatedBLockHeader->size;
+
+			if (headerBegin < this->baseMemory)
+			{
+				//the freed memory is before the base memory so change the base memory
+				//this is the new base memory
+
+				if ((size_t)headerBegin + sizeof(AllocatedBlock) + sizeOfTheFreedBlock == (size_t)this->baseMemory)
+				{
+					//this merges with the current first free block so merge them
+
+					FreeBlock *firstFreeBlock = (FreeBlock *)allocatedBLockHeader;
+					firstFreeBlock->next = ((FreeBlock *)this->baseMemory)->next;
+					firstFreeBlock->size = sizeOfTheFreedBlock + sizeof(FreeBlock) + ((FreeBlock *)this->baseMemory)->size;
+
+					this->baseMemory = (char *)firstFreeBlock;
+
+				}
+				else if ((size_t)headerBegin + sizeof(AllocatedBlock) + sizeOfTheFreedBlock > (size_t)this->baseMemory)
+				{
+					//heap corupted
+					PIKA_PERMA_ASSERT(0, "heap corupted");
+				}
+				else
+				{	//this doesn't merge with the next free block so just link them
+					FreeBlock *firstFreeBlock = (FreeBlock *)allocatedBLockHeader;
+
+					firstFreeBlock->next = this->baseMemory;
+					firstFreeBlock->size = sizeOfTheFreedBlock;
+
+					this->baseMemory = (char *)firstFreeBlock;
+				}
+
+
+			}
+			else
+			{
+				//the freed block is somewhere in the middle
+				//first search for the blocks before and after it
+
+				FreeBlock *current = (FreeBlock *)baseMemory;
+				FreeBlock *next = (FreeBlock *)current->next;
+
+				while (true)
+				{
+
+					if ((current < headerBegin && headerBegin < next)
+						||
+						(current < headerBegin && (next == nullptr || next >= this->end))
+						)
+					{
+						//the block is between 2 blocks
+						FreeBlock *theBlockBefore = current;
+						FreeBlock *theBlockAfter = (FreeBlock *)current->next;
+
+						//merge with the block before
+
+						FreeBlock *newCurent = nullptr;
+						//check if merged
+						if ((size_t)theBlockBefore + sizeof(FreeBlock) + theBlockBefore->size == (size_t)headerBegin)
+						{
+							//merge
+							theBlockBefore->size += sizeof(FreeBlock) + sizeOfTheFreedBlock;
+
+							newCurent = theBlockBefore;
+						}
+						else if ((size_t)theBlockBefore + sizeof(FreeBlock) + theBlockBefore->size > (size_t)headerBegin)
+						{
+							//error heap corupted
+							PIKA_PERMA_ASSERT(0, "heap corupted");
+						}
+						else
+						{
+							//just link, no merge
+							theBlockBefore->next = (char *)headerBegin;
+							FreeBlock *thisBlock = (FreeBlock *)headerBegin;
+							thisBlock->next = nullptr;
+							thisBlock->size = sizeOfTheFreedBlock;
+
+							newCurent = thisBlock;
+						}
+
+						//merge / link with next block
+						if (theBlockAfter != nullptr && theBlockAfter < this->end)
+						{
+
+							if ((size_t)newCurent + sizeof(FreeBlock) + newCurent->size == (size_t)theBlockAfter)
+							{
+								//merge
+								newCurent->size += sizeof(FreeBlock) + theBlockAfter->size;
+								newCurent->next = theBlockAfter->next;
+
+							}
+							else if ((size_t)newCurent + sizeof(FreeBlock) + newCurent->size > (size_t)theBlockAfter)
+							{
+								//err
+								PIKA_PERMA_ASSERT(0, "heap corupted");
+							}
+							else
+							{
+								//just link
+								newCurent->next = (char *)theBlockAfter;
+
+							}
+
+						}
+
+
+						break;
+					}
+
+					current = (FreeBlock *)current->next;
+					next = (FreeBlock *)current->next;
+
+					if (current == nullptr || current >= this->end)
+					{
+						//heap corupted or freed an invalid value
+						PIKA_PERMA_ASSERT(0, "heap corupted or an invalid free");
+
+					}
+				}
+			}
+
+		}
+
+	void *FreeListAllocator::threadSafeAllocate(size_t size)
+		{
+			mu.lock();
+
+			auto a = this->allocate(size);
+
+			mu.unlock();
+
+			return a;
+		}
+
+	void FreeListAllocator::threadSafeFree(void *mem)
+		{
+			if (mem == nullptr) { return; }
+
+			mu.lock();
+
+			this->free(mem);
+
+			mu.unlock();
+		}
+
+	void FreeListAllocator::calculateMemoryMetrics(size_t &availableMemory, size_t &biggestBlock, int &freeBlocks)
+		{
+
+			availableMemory = 0;
+			biggestBlock = 0;
+			freeBlocks = 0;
+
+			if (!baseMemory)
+			{
+				return;
+			}
+
+
+			FreeBlock *last = nullptr;
+			FreeBlock *current = (FreeBlock *)baseMemory;
+
+			while (true)
+			{
+				freeBlocks++;
+				availableMemory += current->size;
+				biggestBlock = std::max(biggestBlock, current->size);
+
+				if (current->next == nullptr || current->next >= this->end)
+				{
+					//that was the last block
+
+					break;
+
+				}
+				else
+				{
+					last = current;
+					current = (FreeBlock *)current->next;
+				}
+
+
+			}
+
+
+
+		}
+
+
+	#ifdef PIKA_WINDOWS
+
+	#define NOMINMAX
+	#include <Windows.h>
+
+	#include <intrin.h>
+	#pragma intrinsic (_InterlockedIncrement)
+	#pragma intrinsic (_InterlockedDecrement)
+
+		//https://preshing.com/20120226/roll-your-own-lightweight-mutex/
+		FreeListAllocatorMutex::FreeListAllocatorMutex()
+		{
+			counter = 0;
+			semaphore = CreateSemaphore(NULL, 0, 1, NULL);
+		}
+
+		void FreeListAllocatorMutex::lock()
+		{
+
+			if (_InterlockedIncrement(&counter) > 1) // x86/64 guarantees acquire semantics
+			{
+				WaitForSingleObject(semaphore, INFINITE);
+			}
+
+		}
+
+		void FreeListAllocatorMutex::unlock()
+		{
+			if (_InterlockedDecrement(&counter) > 0) // x86/64 guarantees release semantics
+			{
+				ReleaseSemaphore(semaphore, 1, NULL);
+			}
+		}
+
+		FreeListAllocatorMutex::~FreeListAllocatorMutex()
+		{
+			CloseHandle(semaphore);
+			counter = 0;
+			semaphore = 0;
+		}
+
+
+	#elif defined(PIKA_LINUX)
+
+		//todo add linux support
+
+	#endif
+
+}
+
+}

+ 92 - 0
Pika/core/pikaSTD/pikaAllocator/freeListAllocator.h

@@ -0,0 +1,92 @@
+//////////////////////////////////////////////////
+//freeListAllocator.h				beta 0.2
+//Copyright(c) 2020 Luta Vlad
+//https://github.com/meemknight/freeListAllocator
+//////////////////////////////////////////////////
+
+#pragma once
+
+#include <pikaConfig.h>
+#include <pikaSizes.h>
+
+namespace pika
+{
+
+namespace allocator
+{
+
+
+
+	#ifdef PIKA_WINDOWS
+	
+		struct FreeListAllocatorMutex
+		{
+		
+			FreeListAllocatorMutex();
+		
+			void lock();
+			void unlock();
+		
+			long counter;
+			void* semaphore;
+		
+			~FreeListAllocatorMutex();
+		
+		};
+	
+	#elif defined(PIKA_LINUX)
+	
+	//todo add linux support
+	
+	#endif
+	
+	
+	
+	struct FreeListAllocator
+	{
+		char* baseMemory = 0;
+	
+		FreeListAllocator() = default;
+		FreeListAllocator(void* baseMemory, size_t memorySize)
+		{
+			init(baseMemory, memorySize);
+		}
+	
+		void init(void* baseMemory, size_t memorySize);
+	
+		void* allocate(size_t size);
+	
+		void free(void* mem);
+	
+		void* threadSafeAllocate(size_t size);
+	
+		void threadSafeFree(void* mem);
+	
+		//available memory is the free memory
+		//biggest block is how large is the biggest free memory block
+		//you can allocate less than the largest biggest free memory because 16 bytes are reserved per block
+		void calculateMemoryMetrics(size_t& availableMemory, size_t& biggestBlock, int& freeBlocks);
+	
+	
+		//if this is false it will crash if it is out of memory
+		//if this is true it will return 0 when there is no more memory
+		//I rocommand leaving this to false
+		bool returnZeroIfNoMoreMemory = false;
+	
+	private:
+	
+		void* end = 0;
+	
+		FreeListAllocatorMutex mu;
+	
+		size_t getEnd()
+		{
+			return (size_t)end;
+		}
+	
+	};
+
+
+};
+
+};

+ 5 - 4
Pika/core/pikaSTD/pikaContext.h

@@ -1,14 +1,15 @@
 #pragma once
 
-//todo remove includes here
-#include "imgui.h"
-#include <GLFW/glfw3.h>
+struct GLFWwindow;
+struct ImGuiContext;
+//void glfwMakeContextCurrent(GLFWwindow *handle)
 
 namespace pika
 {
 	struct PikaContext
 	{
-		using glfwMakeContextCurrent_t = decltype(glfwMakeContextCurrent);
+		//using glfwMakeContextCurrent_t = decltype(glfwMakeContextCurrent);
+		using glfwMakeContextCurrent_t = void(GLFWwindow *);
 
 		glfwMakeContextCurrent_t *glfwMakeContextCurrentPtr = {};
 		GLFWwindow *wind = {};

+ 5 - 0
Pika/core/pikaSTD/pikaSizes.h

@@ -0,0 +1,5 @@
+#pragma once
+
+inline size_t KB(size_t x) { return x * 1024ull; }
+inline size_t MB(size_t x) { return KB(x) * 1024ull; }
+inline size_t GB(size_t x) { return MB(x) * 1024ull; }

+ 1 - 15
Pika/gameplay/dllMain.cpp

@@ -12,28 +12,16 @@ gl2d::Renderer2D renderer;
 PIKA_API void testStart(pika::PikaContext pikaContext)
 {
 	
-	//PIKA_PERMA_ASSERT(glfwInit(), "Problem initializing glfw from dll");
-	//glfwMakeContextCurrent(wind);
 
 	PIKA_PERMA_ASSERT(gladLoadGL(), "Problem initializing glad from dll");
-	//printf("%s\n", glGetString(GL_VERSION));
-	//pika::initImgui(pikaContext);
 
 
 	gl2d::init();
 	renderer.create();
+	pika::setContext(pikaContext);
 
 }
 
-void *userMalloc(size_t sz, void *)
-{
-	return malloc(sz);
-}
-
-void userFree(void *ptr, void *)
-{
-	free(ptr);
-}
 
 PIKA_API void testUpdate(pika::PikaContext pikaContext)
 {
@@ -42,11 +30,9 @@ PIKA_API void testUpdate(pika::PikaContext pikaContext)
 	renderer.renderRectangle({10,10, 100, 100}, Colors_Magenta);
 	renderer.flush();
 
-	//ImGui::SetCurrentContext(imguiContextGlobal);
 
 //	ImGui::SetAllocatorFunctions(userMalloc, userFree);
 
-	pika::setContext(pikaContext);
 
 	ImGui::Begin("test");
 	ImGui::End();