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:
*
* - Allocations < 2048 can still cross boundaries
* - Only operates on one pool (ignores what you pass)
*/
#include <assert.h>
@ -73,6 +74,7 @@ typedef struct {
size_t pool_size; // Size of the memory pool
uint8_t* base_address; // First 2k aligned address 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
* but we need to know the size allocated when we free()...
@ -86,7 +88,7 @@ typedef struct {
static PoolHeader pool_header = {
{0}, NULL, 0, NULL, 0, NULL
{0}, NULL, 0, NULL, 0, false, NULL
};
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) pool;
uint8_t* it = pool_header.block_usage;
uint32_t required_subblocks = (required_size / 256);
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) pool;
struct AllocEntry* it = pool_header.allocations;
struct AllocEntry* last = NULL;
while(it) {
@ -354,17 +360,73 @@ void alloc_free(void* pool, void* p) {
}
void alloc_defrag_start(void* pool) {
(void) pool;
pool_header.defrag_in_progress = true;
}
void* alloc_defrag_address(void* pool, void* p) {
(void) pool;
return p;
}
void alloc_defrag_commit(void* pool) {
(void) pool;
pool_header.defrag_in_progress = false;
}
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);
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_base_address(void* pool);
size_t alloc_block_count(void* pool);

View File

@ -12,7 +12,7 @@
#define CLIP_DEBUG 0
#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 SDL_Window* WINDOW = NULL;

View File

@ -8,7 +8,7 @@
#include "config.h"
#include "platform.h"
#include "yalloc/yalloc.h"
#include "alloc/alloc.h"
/* We always leave this amount of vram unallocated to prevent
* 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 GLboolean TEXTURE_TWIDDLE_ENABLED = GL_FALSE;
static void* YALLOC_BASE = NULL;
static size_t YALLOC_SIZE = 0;
static void* ALLOC_BASE = NULL;
static size_t ALLOC_SIZE = 0;
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) {
void* ret = yalloc_alloc(YALLOC_BASE, size);
static void* alloc_malloc_and_defrag(size_t size) {
void* ret = alloc_malloc(ALLOC_BASE, size);
if(!ret) {
/* Tried to allocate, but out of room, let's try defragging
* and repeating the alloc */
fprintf(stderr, "Ran out of memory, defragmenting\n");
glDefragmentTextureMemory_KOS();
ret = yalloc_alloc(YALLOC_BASE, size);
ret = alloc_malloc(ALLOC_BASE, size);
}
gl_assert(ret && "Out of PVR memory!");
@ -505,15 +505,15 @@ GLubyte _glInitTextures() {
//memset((void*) SUBBANKS_USED, 0x0, sizeof(SUBBANKS_USED));
size_t vram_free = GPUMemoryAvailable();
YALLOC_SIZE = vram_free - PVR_MEM_BUFFER_SIZE; /* Take all but 64kb VRAM */
YALLOC_BASE = GPUMemoryAlloc(YALLOC_SIZE);
ALLOC_SIZE = vram_free - PVR_MEM_BUFFER_SIZE; /* Take all but 64kb VRAM */
ALLOC_BASE = GPUMemoryAlloc(ALLOC_SIZE);
#ifdef __DREAMCAST__
/* Ensure memory is aligned */
gl_assert((uintptr_t) YALLOC_BASE % 32 == 0);
#endif
yalloc_init(YALLOC_BASE, YALLOC_SIZE);
alloc_init(ALLOC_BASE, ALLOC_SIZE);
gl_assert(TEXTURE_OBJECTS.element_size > 0);
return 1;
@ -610,7 +610,7 @@ void APIENTRY glDeleteTextures(GLsizei n, GLuint *textures) {
}
if(txr->data) {
yalloc_free(YALLOC_BASE, txr->data);
alloc_free(ALLOC_BASE, txr->data);
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 */
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
@ -1242,14 +1242,14 @@ void _glAllocateSpaceForMipmaps(TextureObject* active) {
memcpy(temp, active->data, size);
/* Free the PVR data */
yalloc_free(YALLOC_BASE, active->data);
alloc_free(ALLOC_BASE, active->data);
active->data = NULL;
}
/* Figure out how much room to allocate for mipmaps */
GLuint bytes = _glGetMipmapDataSize(active);
active->data = yalloc_alloc_and_defrag(bytes);
active->data = alloc_malloc_and_defrag(bytes);
gl_assert(active->data);
@ -1389,7 +1389,7 @@ void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat,
active->height != height ||
active->internalFormat != cleanInternalFormat) {
/* changed - free old texture memory */
yalloc_free(YALLOC_BASE, active->data);
alloc_free(ALLOC_BASE, active->data);
active->data = NULL;
active->mipmap = 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 */
_glAllocateSpaceForMipmaps(active);
} else {
active->data = yalloc_alloc_and_defrag(active->baseDataSize);
active->data = alloc_malloc_and_defrag(active->baseDataSize);
}
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");
}
GLuint _glMaxTextureMemory() {
return YALLOC_SIZE;
return ALLOC_SIZE;
}
GLuint _glFreeTextureMemory() {
return yalloc_count_free(YALLOC_BASE);
return alloc_count_free(ALLOC_BASE);
}
GLuint _glUsedTextureMemory() {
return YALLOC_SIZE - _glFreeTextureMemory();
return ALLOC_SIZE - _glFreeTextureMemory();
}
GLuint _glFreeContiguousTextureMemory() {
return yalloc_count_continuous(YALLOC_BASE);
return alloc_count_continuous(ALLOC_BASE);
}
GLAPI GLvoid APIENTRY glDefragmentTextureMemory_KOS(void) {
yalloc_defrag_start(YALLOC_BASE);
alloc_defrag_start(ALLOC_BASE);
GLuint id;
@ -1881,11 +1881,11 @@ GLAPI GLvoid APIENTRY glDefragmentTextureMemory_KOS(void) {
TextureObject* txr = (TextureObject*) named_array_get(&TEXTURE_OBJECTS, id);
if(txr){
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) {

View File

@ -16,7 +16,7 @@ static inline int round_up(int n, int multiple)
class AllocatorTests : public test::TestCase {
public:
uint8_t pool[16 * 2048];
uint8_t __attribute__((aligned(2048))) pool[16 * 2048];
void set_up() {
}
@ -32,7 +32,7 @@ public:
assert_equal(alloc_next_available(pool, 16), 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(expected_base_address)
) / 2048;
@ -43,7 +43,7 @@ public:
void test_alloc_malloc() {
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);
/* First alloc should always be the base address */