Switch to the new allocator

This commit is contained in:
Luke Benstead 2023-08-31 21:21:14 +01:00
parent fd9a9d1c25
commit 3248499d5a
5 changed files with 97 additions and 32 deletions

View File

@ -41,6 +41,7 @@
* FIXME: * FIXME:
* *
* - Allocations < 2048 can still cross boundaries * - Allocations < 2048 can still cross boundaries
* - Only operates on one pool (ignores what you pass)
*/ */
#include <assert.h> #include <assert.h>
@ -73,6 +74,7 @@ typedef struct {
size_t pool_size; // Size of the memory pool size_t pool_size; // Size of the memory pool
uint8_t* base_address; // First 2k aligned address in the pool uint8_t* base_address; // First 2k aligned address in the pool
size_t block_count; // Number of 2k blocks in the pool size_t block_count; // Number of 2k blocks in the pool
bool defrag_in_progress;
/* It's frustrating that we need to do this dynamically /* It's frustrating that we need to do this dynamically
* but we need to know the size allocated when we free()... * but we need to know the size allocated when we free()...
@ -86,7 +88,7 @@ typedef struct {
static PoolHeader pool_header = { static PoolHeader pool_header = {
{0}, NULL, 0, NULL, 0, NULL {0}, NULL, 0, NULL, 0, false, NULL
}; };
void* alloc_base_address(void* pool) { void* alloc_base_address(void* pool) {
@ -100,6 +102,8 @@ size_t alloc_block_count(void* pool) {
} }
void* alloc_next_available(void* pool, size_t required_size) { void* alloc_next_available(void* pool, size_t required_size) {
(void) pool;
uint8_t* it = pool_header.block_usage; uint8_t* it = pool_header.block_usage;
uint32_t required_subblocks = (required_size / 256); uint32_t required_subblocks = (required_size / 256);
if(required_size % 256) required_subblocks += 1; if(required_size % 256) required_subblocks += 1;
@ -301,6 +305,8 @@ void* alloc_malloc(void* pool, size_t size) {
} }
void alloc_free(void* pool, void* p) { void alloc_free(void* pool, void* p) {
(void) pool;
struct AllocEntry* it = pool_header.allocations; struct AllocEntry* it = pool_header.allocations;
struct AllocEntry* last = NULL; struct AllocEntry* last = NULL;
while(it) { while(it) {
@ -354,17 +360,73 @@ void alloc_free(void* pool, void* p) {
} }
void alloc_defrag_start(void* pool) { void alloc_defrag_start(void* pool) {
(void) pool;
pool_header.defrag_in_progress = true;
} }
void* alloc_defrag_address(void* pool, void* p) { void* alloc_defrag_address(void* pool, void* p) {
(void) pool;
return p;
} }
void alloc_defrag_commit(void* pool) { void alloc_defrag_commit(void* pool) {
(void) pool;
pool_header.defrag_in_progress = false;
} }
bool alloc_defrag_in_progress(void* pool) { bool alloc_defrag_in_progress(void* pool) {
(void) pool;
return pool_header.defrag_in_progress;
}
static inline uint8_t count_ones(uint8_t byte) {
static const uint8_t NIBBLE_LOOKUP [16] = {
0, 1, 1, 2, 1, 2, 2, 3,
1, 2, 2, 3, 2, 3, 3, 4
};
return NIBBLE_LOOKUP[byte & 0x0F] + NIBBLE_LOOKUP[byte >> 4];
}
size_t alloc_count_free(void* pool) {
uint8_t* it = pool_header.block_usage;
uint8_t* end = it + pool_header.block_count;
size_t total_free = 0;
while(it < end) {
total_free += count_ones(*it) * 256;
++it;
}
return total_free;
}
size_t alloc_count_continuous(void* pool) {
(void) pool;
size_t largest_block = 0;
uint8_t* it = pool_header.block_usage;
uint8_t* end = it + pool_header.block_count;
size_t current_block = 0;
while(it < end) {
uint8_t t = *it++;
if(!t) {
current_block += 2048;
} else {
for(int i = 7; i >= 0; --i) {
bool bitset = (t & (1 << i));
if(bitset) {
current_block += (7 - i) * 256;
if(largest_block < current_block) {
largest_block = current_block;
current_block = 0;
}
}
}
}
}
return largest_block;
} }

View File

@ -19,6 +19,9 @@ void* alloc_defrag_address(void* pool, void* p);
void alloc_defrag_commit(void* pool); void alloc_defrag_commit(void* pool);
bool alloc_defrag_in_progress(void* pool); bool alloc_defrag_in_progress(void* pool);
size_t alloc_count_free(void* pool);
size_t alloc_count_continuous(void* pool);
void* alloc_next_available(void* pool, size_t required_size); void* alloc_next_available(void* pool, size_t required_size);
void* alloc_base_address(void* pool); void* alloc_base_address(void* pool);
size_t alloc_block_count(void* pool); size_t alloc_block_count(void* pool);

View File

@ -12,7 +12,7 @@
#define CLIP_DEBUG 0 #define CLIP_DEBUG 0
#define ZNEAR_CLIPPING_ENABLED 1 #define ZNEAR_CLIPPING_ENABLED 1
static size_t AVAILABLE_VRAM = 16 * 1024 * 1024; static size_t AVAILABLE_VRAM = 8 * 1024 * 1024;
static Matrix4x4 MATRIX; static Matrix4x4 MATRIX;
static SDL_Window* WINDOW = NULL; static SDL_Window* WINDOW = NULL;

View File

@ -8,7 +8,7 @@
#include "config.h" #include "config.h"
#include "platform.h" #include "platform.h"
#include "yalloc/yalloc.h" #include "alloc/alloc.h"
/* We always leave this amount of vram unallocated to prevent /* We always leave this amount of vram unallocated to prevent
* issues with the allocator */ * issues with the allocator */
@ -31,8 +31,8 @@ static GLboolean SUBBANKS_USED[MAX_GLDC_PALETTE_SLOTS][MAX_GLDC_4BPP_PALETTE_SLO
static GLenum INTERNAL_PALETTE_FORMAT = GL_RGBA8; static GLenum INTERNAL_PALETTE_FORMAT = GL_RGBA8;
static GLboolean TEXTURE_TWIDDLE_ENABLED = GL_FALSE; static GLboolean TEXTURE_TWIDDLE_ENABLED = GL_FALSE;
static void* YALLOC_BASE = NULL; static void* ALLOC_BASE = NULL;
static size_t YALLOC_SIZE = 0; static size_t ALLOC_SIZE = 0;
static const unsigned short MortonTable256[256] = static const unsigned short MortonTable256[256] =
{ {
@ -84,15 +84,15 @@ GL_FORCE_INLINE uint32_t twid_location(uint32_t i, uint32_t w, uint32_t h) {
} }
static void* yalloc_alloc_and_defrag(size_t size) { static void* alloc_malloc_and_defrag(size_t size) {
void* ret = yalloc_alloc(YALLOC_BASE, size); void* ret = alloc_malloc(ALLOC_BASE, size);
if(!ret) { if(!ret) {
/* Tried to allocate, but out of room, let's try defragging /* Tried to allocate, but out of room, let's try defragging
* and repeating the alloc */ * and repeating the alloc */
fprintf(stderr, "Ran out of memory, defragmenting\n"); fprintf(stderr, "Ran out of memory, defragmenting\n");
glDefragmentTextureMemory_KOS(); glDefragmentTextureMemory_KOS();
ret = yalloc_alloc(YALLOC_BASE, size); ret = alloc_malloc(ALLOC_BASE, size);
} }
gl_assert(ret && "Out of PVR memory!"); gl_assert(ret && "Out of PVR memory!");
@ -505,15 +505,15 @@ GLubyte _glInitTextures() {
//memset((void*) SUBBANKS_USED, 0x0, sizeof(SUBBANKS_USED)); //memset((void*) SUBBANKS_USED, 0x0, sizeof(SUBBANKS_USED));
size_t vram_free = GPUMemoryAvailable(); size_t vram_free = GPUMemoryAvailable();
YALLOC_SIZE = vram_free - PVR_MEM_BUFFER_SIZE; /* Take all but 64kb VRAM */ ALLOC_SIZE = vram_free - PVR_MEM_BUFFER_SIZE; /* Take all but 64kb VRAM */
YALLOC_BASE = GPUMemoryAlloc(YALLOC_SIZE); ALLOC_BASE = GPUMemoryAlloc(ALLOC_SIZE);
#ifdef __DREAMCAST__ #ifdef __DREAMCAST__
/* Ensure memory is aligned */ /* Ensure memory is aligned */
gl_assert((uintptr_t) YALLOC_BASE % 32 == 0); gl_assert((uintptr_t) YALLOC_BASE % 32 == 0);
#endif #endif
yalloc_init(YALLOC_BASE, YALLOC_SIZE); alloc_init(ALLOC_BASE, ALLOC_SIZE);
gl_assert(TEXTURE_OBJECTS.element_size > 0); gl_assert(TEXTURE_OBJECTS.element_size > 0);
return 1; return 1;
@ -610,7 +610,7 @@ void APIENTRY glDeleteTextures(GLsizei n, GLuint *textures) {
} }
if(txr->data) { if(txr->data) {
yalloc_free(YALLOC_BASE, txr->data); alloc_free(ALLOC_BASE, txr->data);
txr->data = NULL; txr->data = NULL;
} }
@ -858,10 +858,10 @@ void APIENTRY glCompressedTexImage2DARB(GLenum target,
/* Odds are slim new data is same size as old, so free always */ /* Odds are slim new data is same size as old, so free always */
if(active->data) { if(active->data) {
yalloc_free(YALLOC_BASE, active->data); alloc_free(ALLOC_BASE, active->data);
} }
active->data = yalloc_alloc_and_defrag(imageSize); active->data = alloc_malloc_and_defrag(imageSize);
gl_assert(active->data); // Debug assert gl_assert(active->data); // Debug assert
@ -1242,14 +1242,14 @@ void _glAllocateSpaceForMipmaps(TextureObject* active) {
memcpy(temp, active->data, size); memcpy(temp, active->data, size);
/* Free the PVR data */ /* Free the PVR data */
yalloc_free(YALLOC_BASE, active->data); alloc_free(ALLOC_BASE, active->data);
active->data = NULL; active->data = NULL;
} }
/* Figure out how much room to allocate for mipmaps */ /* Figure out how much room to allocate for mipmaps */
GLuint bytes = _glGetMipmapDataSize(active); GLuint bytes = _glGetMipmapDataSize(active);
active->data = yalloc_alloc_and_defrag(bytes); active->data = alloc_malloc_and_defrag(bytes);
gl_assert(active->data); gl_assert(active->data);
@ -1389,7 +1389,7 @@ void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat,
active->height != height || active->height != height ||
active->internalFormat != cleanInternalFormat) { active->internalFormat != cleanInternalFormat) {
/* changed - free old texture memory */ /* changed - free old texture memory */
yalloc_free(YALLOC_BASE, active->data); alloc_free(ALLOC_BASE, active->data);
active->data = NULL; active->data = NULL;
active->mipmap = 0; active->mipmap = 0;
active->mipmapCount = 0; active->mipmapCount = 0;
@ -1434,7 +1434,7 @@ void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat,
/* If we're uploading a mipmap level, we need to allocate the full amount of space */ /* If we're uploading a mipmap level, we need to allocate the full amount of space */
_glAllocateSpaceForMipmaps(active); _glAllocateSpaceForMipmaps(active);
} else { } else {
active->data = yalloc_alloc_and_defrag(active->baseDataSize); active->data = alloc_malloc_and_defrag(active->baseDataSize);
} }
active->isCompressed = GL_FALSE; active->isCompressed = GL_FALSE;
@ -1856,23 +1856,23 @@ GLAPI void APIENTRY glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height
gl_assert(0 && "Not Implemented"); gl_assert(0 && "Not Implemented");
} }
GLuint _glMaxTextureMemory() { GLuint _glMaxTextureMemory() {
return YALLOC_SIZE; return ALLOC_SIZE;
} }
GLuint _glFreeTextureMemory() { GLuint _glFreeTextureMemory() {
return yalloc_count_free(YALLOC_BASE); return alloc_count_free(ALLOC_BASE);
} }
GLuint _glUsedTextureMemory() { GLuint _glUsedTextureMemory() {
return YALLOC_SIZE - _glFreeTextureMemory(); return ALLOC_SIZE - _glFreeTextureMemory();
} }
GLuint _glFreeContiguousTextureMemory() { GLuint _glFreeContiguousTextureMemory() {
return yalloc_count_continuous(YALLOC_BASE); return alloc_count_continuous(ALLOC_BASE);
} }
GLAPI GLvoid APIENTRY glDefragmentTextureMemory_KOS(void) { GLAPI GLvoid APIENTRY glDefragmentTextureMemory_KOS(void) {
yalloc_defrag_start(YALLOC_BASE); alloc_defrag_start(ALLOC_BASE);
GLuint id; GLuint id;
@ -1881,11 +1881,11 @@ GLAPI GLvoid APIENTRY glDefragmentTextureMemory_KOS(void) {
TextureObject* txr = (TextureObject*) named_array_get(&TEXTURE_OBJECTS, id); TextureObject* txr = (TextureObject*) named_array_get(&TEXTURE_OBJECTS, id);
if(txr){ if(txr){
gl_assert(txr->index == id); gl_assert(txr->index == id);
txr->data = yalloc_defrag_address(YALLOC_BASE, txr->data); txr->data = alloc_defrag_address(ALLOC_BASE, txr->data);
} }
} }
yalloc_defrag_commit(YALLOC_BASE); alloc_defrag_commit(ALLOC_BASE);
} }
GLAPI void APIENTRY glGetTexImage(GLenum tex, GLint lod, GLenum format, GLenum type, GLvoid* img) { GLAPI void APIENTRY glGetTexImage(GLenum tex, GLint lod, GLenum format, GLenum type, GLvoid* img) {

View File

@ -16,7 +16,7 @@ static inline int round_up(int n, int multiple)
class AllocatorTests : public test::TestCase { class AllocatorTests : public test::TestCase {
public: public:
uint8_t pool[16 * 2048]; uint8_t __attribute__((aligned(2048))) pool[16 * 2048];
void set_up() { void set_up() {
} }
@ -32,7 +32,7 @@ public:
assert_equal(alloc_next_available(pool, 16), expected_base_address); assert_equal(alloc_next_available(pool, 16), expected_base_address);
assert_equal(alloc_base_address(pool), expected_base_address); assert_equal(alloc_base_address(pool), expected_base_address);
int expected_blocks = ( size_t expected_blocks = (
uintptr_t(pool + sizeof(pool)) - uintptr_t(pool + sizeof(pool)) -
uintptr_t(expected_base_address) uintptr_t(expected_base_address)
) / 2048; ) / 2048;
@ -43,7 +43,7 @@ public:
void test_alloc_malloc() { void test_alloc_malloc() {
alloc_init(pool, sizeof(pool)); alloc_init(pool, sizeof(pool));
void* base_address = alloc_base_address(pool); uint8_t* base_address = (uint8_t*) alloc_base_address(pool);
void* a1 = alloc_malloc(pool, 1024); void* a1 = alloc_malloc(pool, 1024);
/* First alloc should always be the base address */ /* First alloc should always be the base address */