From a2c19cf65dc4d3ffe4043707e4adb74f45091202 Mon Sep 17 00:00:00 2001 From: Klemen Plestenjak Date: Wed, 13 Dec 2023 09:31:08 +0100 Subject: [PATCH] Add page support to object pool --- engine/breeze/util/object_pool.c | 157 ++++++++++++++++++++++-------- engine/breeze/util/object_pool.h | 12 ++- engine/breeze/util/spatial_grid.c | 2 +- game/main.c | 4 +- game/map_init.c | 2 +- 5 files changed, 129 insertions(+), 48 deletions(-) diff --git a/engine/breeze/util/object_pool.c b/engine/breeze/util/object_pool.c index c56fe0d..412ecc0 100644 --- a/engine/breeze/util/object_pool.c +++ b/engine/breeze/util/object_pool.c @@ -2,13 +2,65 @@ #include "../memory/memory.h" +typedef union BzObjectIdx { + struct { + u16 idx; + u16 page; + }; + u32 value; +} BzObjectIdx; + +#define INVALID_OBJECT_IDX (UINT32_MAX) + +static_assert(sizeof(BzObjectIdx) == 4, ""); + typedef struct BzObjectPool { - void *objects; + void **pages; + size_t pageCapacity; + size_t pageSize; + size_t objectsPerPage; size_t stride; - size_t numObjects; - i32 firstFree; + + size_t numFree; + BzObjectIdx firstFree; + + BzObjectPoolFunc constructor; + BzObjectPoolFunc destructor; } BzObjectPool; +static void initializePage(BzObjectPool *pool, u16 pageIdx) { + BZ_ASSERT(pageIdx < UINT8_MAX); + // Link free list + for (size_t i = 0; i < pool->objectsPerPage - 1; i++) { + BzObjectIdx idx = {.page = pageIdx, .idx = i}; + BzObjectIdx *obj = bzObjectPoolGetObject(pool, idx.value); + + BzObjectIdx nextIdx = {.page = pageIdx, .idx = i + 1}; + obj->value = nextIdx.value; + } + BzObjectIdx lastIdx = {.page = pageIdx, .idx = pool->objectsPerPage - 1}; + BzObjectIdx *lastObj = bzObjectPoolGetObject(pool, lastIdx.value); + lastObj->value = pool->firstFree.value; + pool->firstFree = (BzObjectIdx) {.page = pageIdx, .idx = 0}; + + pool->numFree += pool->objectsPerPage; +} +static void addNewPage(BzObjectPool *pool) { + if (pool->pageSize >= pool->pageCapacity) { + size_t newCapacity = pool->pageCapacity << 1; + void **newPages = bzResize(pool->pages, sizeof(*newPages) * newCapacity); + BZ_ASSERT(newPages); + pool->pages = newPages; + pool->pageCapacity = newCapacity; + } + void *newPage = bzAlloc(pool->stride * pool->objectsPerPage); + BZ_ASSERT(newPage); + size_t pageIdx = pool->pageSize; + pool->pages[pageIdx] = newPage; + pool->pageSize++; + initializePage(pool, pageIdx); +} + BzObjectPool *bzObjectPoolCreate(const BzObjectPoolDesc *desc) { BZ_ASSERT(desc->objectSize > 0); @@ -18,58 +70,83 @@ BzObjectPool *bzObjectPoolCreate(const BzObjectPoolDesc *desc) { if (stride < sizeof(i32)) { stride = sizeof(i32); } + BZ_ASSERT(desc->objectsPerPage > 0); + BZ_ASSERT(desc->objectsPerPage < (2 << 23)); - size_t numBytes = sizeof(BzObjectPool) + desc->numObjects * stride; - BzObjectPool *pool = bzAlloc(numBytes); + size_t objectsPerPage = desc->objectsPerPage; + + void *firstPage = bzAlloc(stride * objectsPerPage); + BZ_ASSERT(firstPage); + + size_t pageCapacity = 8; + void **pages = bzAlloc(sizeof(*pages) * pageCapacity); + BZ_ASSERT(pages); + pages[0] = firstPage; + + BzObjectPool *pool = bzAlloc(sizeof(*pool)); *pool = (BzObjectPool) { - .objects=pool + 1, - .stride=stride, - .numObjects=desc->numObjects, - .firstFree=0, + .pages = pages, + .pageSize = 0, + .pageCapacity = pageCapacity, + .objectsPerPage = objectsPerPage, + .stride = stride, }; + pool->firstFree.value = INVALID_OBJECT_IDX; - // Link free list - for (size_t i = 0; i < pool->numObjects - 1; i++) { - i32 *object = bzObjectPoolGetObject(pool, i); - *object = (i32) (i + 1); - } - i32 *lastObject = bzObjectPoolGetObject(pool, pool->numObjects - 1); - *lastObject = -1; - + addNewPage(pool); return pool; } void bzObjectPoolDestroy(BzObjectPool *pool) { bzFree(pool); } -size_t bzObjectPoolCalcNumFree(BzObjectPool *pool) { - size_t count = 0; - i32 idx = pool->firstFree; - while (idx != -1) { - count++; - i32 *object = bzObjectPoolGetObject(pool, idx); - idx = *object; - } - return count; +size_t bzObjectPoolGetNumFree(BzObjectPool *pool) { + return pool->numFree; } void *bzObjectPool(BzObjectPool *pool) { - if (pool->firstFree == -1) - return NULL; - i32 *object = bzObjectPoolGetObject(pool, pool->firstFree); - pool->firstFree = *object; + if (pool->firstFree.value == INVALID_OBJECT_IDX) { + addNewPage(pool); + BZ_ASSERT(pool->firstFree.value != INVALID_OBJECT_IDX); + } + BzObjectIdx *object = (BzObjectIdx *) bzObjectPoolGetObject(pool, pool->firstFree.value); + pool->firstFree = *object; + + BZ_ASSERT(pool->numFree > 0); + pool->numFree--; return object; } -void *bzObjectPoolGetObject(BzObjectPool *pool, i32 idx) { - BZ_ASSERT(idx >= 0 && (size_t) idx < pool->numObjects); - return (void *) ((u8 *) pool->objects + idx * pool->stride); +void *bzObjectPoolGetObject(BzObjectPool *pool, u32 idx) { + BzObjectIdx objectIdx = {.value = idx}; + BZ_ASSERT(objectIdx.page >= 0 && objectIdx.page < pool->pageSize); + BZ_ASSERT(objectIdx.idx >= 0 && objectIdx.idx < pool->objectsPerPage); + + void *page = pool->pages[objectIdx.page]; + return (void *) ((u8 *) page + objectIdx.idx * pool->stride); } -i32 bzObjectPoolGetIdx(BzObjectPool *pool, void *object) { - size_t objectIdx = (size_t) object - (size_t) pool->objects; - return (i32) (objectIdx / pool->stride); +u32 bzObjectPoolGetIdx(BzObjectPool *pool, void *object) { + u16 pageIdx = pool->pageSize; + u16 idx = 0; + + for (size_t i = 0; i < pool->pageSize; i++) { + void *start = pool->pages[i]; + void *end = (u8 *) start + pool->objectsPerPage * pool->stride; + + if (object >= start && object < end) { + pageIdx = i; + idx = (size_t) object - (size_t) start; + idx = idx / pool->stride; + break; + } + } + BZ_ASSERT(pageIdx < pool->pageSize); + BzObjectIdx objectIdx = {.page = pageIdx, .idx = idx}; + return objectIdx.value; } void bzObjectPoolRelease(BzObjectPool *pool, void *object) { - size_t objectIdx = bzObjectPoolGetIdx(pool, object); - BZ_ASSERT(objectIdx < pool->numObjects); - *(i32 *) ((u8 *) pool->objects + objectIdx * pool->stride) = pool->firstFree; - pool->firstFree = (i32) objectIdx; + BzObjectIdx objectIdx = {.value = bzObjectPoolGetIdx(pool, object)}; + + void *page = pool->pages[objectIdx.page]; + *(BzObjectIdx *) ((u8 *) page + objectIdx.idx * pool->stride) = pool->firstFree; + pool->firstFree = objectIdx; + pool->numFree++; } diff --git a/engine/breeze/util/object_pool.h b/engine/breeze/util/object_pool.h index f33a4a2..4463d9c 100644 --- a/engine/breeze/util/object_pool.h +++ b/engine/breeze/util/object_pool.h @@ -5,18 +5,22 @@ typedef struct BzObjectPool BzObjectPool; +typedef void (*BzObjectPoolFunc)(void *object); + typedef struct BzObjectPoolDesc { size_t objectSize; - size_t numObjects; + size_t objectsPerPage; + BzObjectPoolFunc constructor; + BzObjectPoolFunc destructor; } BzObjectPoolDesc; BzObjectPool *bzObjectPoolCreate(const BzObjectPoolDesc *desc); void bzObjectPoolDestroy(BzObjectPool *pool); -size_t bzObjectPoolCalcNumFree(BzObjectPool *pool); +size_t bzObjectPoolGetNumFree(BzObjectPool *pool); void *bzObjectPool(BzObjectPool *pool); -void *bzObjectPoolGetObject(BzObjectPool *pool, i32 idx); -i32 bzObjectPoolGetIdx(BzObjectPool *pool, void *object); +void *bzObjectPoolGetObject(BzObjectPool *pool, u32 idx); +u32 bzObjectPoolGetIdx(BzObjectPool *pool, void *object); void bzObjectPoolRelease(BzObjectPool *pool, void *object); #endif //BREEZE_OBJECT_POOL_H diff --git a/engine/breeze/util/spatial_grid.c b/engine/breeze/util/spatial_grid.c index bc8e88a..898873a 100644 --- a/engine/breeze/util/spatial_grid.c +++ b/engine/breeze/util/spatial_grid.c @@ -42,7 +42,7 @@ BzSpatialGrid *bzSpatialGridCreate(const BzSpatialGridDesc *desc) { } grid->entriesPool = bzObjectPoolCreate(&(BzObjectPoolDesc) { .objectSize=sizeof(BzSpatialGridEntry) + desc->userDataSize, - .numObjects=1024, + .objectsPerPage=1024, }); return grid; } diff --git a/game/main.c b/game/main.c index 129c6f7..866905c 100644 --- a/game/main.c +++ b/game/main.c @@ -79,7 +79,7 @@ bool init(void *userData) { // init pools game->pools.pathData = bzObjectPoolCreate(&(BzObjectPoolDesc) { .objectSize=sizeof(PathData), - .numObjects=512 + .objectsPerPage=512 }); @@ -241,7 +241,7 @@ void imguiRender(float dt, void *userData) { createWorker((Position) {1100, 400}, (Size) {10, 10}, game->entityGrid, &game->map.tilesets[2], 1322); } - igText("Num paths from pool available: %llu", bzObjectPoolCalcNumFree(game->pools.pathData)); + igText("Num paths from pool available: %llu", bzObjectPoolGetNumFree(game->pools.pathData)); const char *inputState = "NONE"; switch (input->state) { case INPUT_NONE: diff --git a/game/map_init.c b/game/map_init.c index 75598f7..5174459 100644 --- a/game/map_init.c +++ b/game/map_init.c @@ -107,7 +107,7 @@ bool initTreesLayer(BzTileMap *map, BzTileLayer *layer) { f32 posX = layer->offsetX + x * sizeX; f32 posY = layer->offsetY + y * sizeY; ecs_entity_t e = ecs_new_id(ECS); - //bzSpatialGridInsert(game->entityGrid, &e, posX, posY, sizeX, sizeY);; + bzSpatialGridInsert(game->entityGrid, &e, posX, posY, sizeX, sizeY); posX += sizeX * 0.5f; posY += sizeY * 0.5f; ecs_add(ECS, e, TextureTerrain);