From 43bdfeed0c814d0dcb84731c738573a0a03c0218 Mon Sep 17 00:00:00 2001 From: cinnaboot Date: Thu, 7 Oct 2021 11:21:28 -0400 Subject: [PATCH] get linear memory allocation working In hindsight, this was a dumb idea. I basically made a binary file format with headers pointing to offsets in the memory block. It would be easier carve up the memory block for arrays of data structures with pointers to other arrays of (eg. glm::vec3), like a normal arena allocator. On the plus side, I think I came up with a new anti-pattern. --- examples/render_groups/main.cpp | 12 +++ include/renderer.h | 24 ++++-- src/renderer.cpp | 142 ++++++++++++++++++++++++++++++++ 3 files changed, 170 insertions(+), 8 deletions(-) diff --git a/examples/render_groups/main.cpp b/examples/render_groups/main.cpp index 0e78360..24adad9 100644 --- a/examples/render_groups/main.cpp +++ b/examples/render_groups/main.cpp @@ -126,6 +126,7 @@ main() renAddLight(rs, glm::vec3()); renAddLight(rs, glm::vec3()); +#if 0 // FIXME: this introduces a potential buffer overrun. Need to implement // a memory manager for render_groups, eg: // renPushGroup(rs, new_group) @@ -174,6 +175,17 @@ main() entInitMesh(square, sm, GL_LINE_LOOP); renDoRenderLoop(rs, 60, doFrameCallbackPre); +#endif + //rs->assets = memInitBlock(INITIAL_ASSET_BLOCK_SIZE); + rs->assets = memInitBlock(10 * 1024 * 1024); // NOTE: 10MB + assert(rs->assets != nullptr); + + // load assets manually + render_asset* icosphere = memPushAsset(rs->assets, "../data/icosphere.glb"); + assert(icosphere != nullptr); + + render_asset* ship = memPushAsset(rs->assets, "../data/spaceship.glb"); + assert(ship != nullptr); renShutdown(rs); return 0; diff --git a/include/renderer.h b/include/renderer.h index 392ff3d..a5e6d47 100644 --- a/include/renderer.h +++ b/include/renderer.h @@ -47,32 +47,33 @@ struct SDL_Handles struct mesh_asset { // header - size_t size; + uint asset_size; uint num_vertices; uint num_indices; mesh_asset* next; // data + glm::mat4 model_transform; glm::vec3* vertices; uint* indices; glm::vec3* texture_coords; - glm::mat4* model_transform; }; #define MAX_PATH_SIZE 256 struct render_asset { // header - size_t size; + uint total_size; uint num_meshes; uint name_len; render_asset* next; // data char* filepath; - mesh_asset* meshes; + mesh_asset* head_mesh; // FIXME: also need to flatten diffuse_texture->pixels and track size of // util_image structure, ie) another heaader + pixels + //uint tex_size; util_image* diffuse_texture; // FIXME: node_animation has a pointer to a render_object. need to decouple // that somehow @@ -81,12 +82,16 @@ struct render_asset // NOTE: This is a linearly allocated block to contain all used mesh assets // Assets are loaded once, and then copied to video memory as needed. +// NOTE: sub-data structures are implemented as a write only single linked list, +// with no insertion or moving of items +// https://en.wikipedia.org/wiki/Region-based_memory_management +// http://cslibrary.stanford.edu/103/LinkedListBasics.pdf struct asset_memory_block { - uint asset_count; size_t max_size; - render_asset* first; - render_asset* next_free; + render_asset* head; + render_asset* tail; + uint asset_count; }; #define INITIAL_ASSET_BLOCK_SIZE 256 * 1024 * 1024 // 256MB @@ -94,7 +99,7 @@ asset_memory_block* memInitBlock(size_t initial_size = INITIAL_ASSET_BLOCK_SIZE); render_asset* -memAddAsset(const char* filepath); +memPushAsset(asset_memory_block* block, const char* filepath); void memFreeBlock(asset_memory_block* block); @@ -115,6 +120,9 @@ struct render_state render_group** render_groups; uint render_group_count; + // WIP + asset_memory_block* assets; + // TODO: hide shaders behind a better abstraction than 'shader_wrapper' default_shader_program* default_shader; simple_shader_program* simple_shader; diff --git a/src/renderer.cpp b/src/renderer.cpp index 8430bcc..13b3a9d 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -37,6 +37,146 @@ void setDefaults(render_state* rs, glm::vec2 viewport_dims); // interface +// WIP +size_t +getBlockFreeSpace(asset_memory_block* block) +{ + return (uint8_t*) block + + block->max_size + - (uint8_t*) block->tail + + block->tail->total_size; +} + +uint +getAssetSize(const mesh_group& mg, uint name_len) +{ + uint total = sizeof(render_asset); + total += name_len; + + for (uint i = 0; i < mg.num_meshes; i++) { + mesh_info* mi = mg.meshes[i]; + total += sizeof(mesh_asset); + // NOTE: vertices, normals, and tex coords + total += 3 * mi->num_vertices * sizeof(glm::vec3); + total += mi->num_indices * sizeof(uint); + } + + return total; +} + +void* +getAddressOffset(void* address, uint offset) +{ + return (void*) ((uint8_t*) address + offset); +} + +// TODO: replace mesh_info structure +mesh_asset* +initMeshAsset(mesh_asset* mesh, mesh_info* mi) +{ + mesh->num_vertices = mi->num_vertices; + mesh->num_indices = mi->num_indices; + mesh->asset_size = sizeof(mesh_asset); + mesh->vertices = (glm::vec3*) ((uint8_t*) mesh + mesh->asset_size); + // NOTE: vertices, normals, and tex coords + mesh->asset_size += 3 * mesh->num_vertices * sizeof(glm::vec3); + mesh->asset_size += mesh->num_indices * sizeof(uint); + mesh->asset_size += sizeof(glm::mat4); + // FIXME: this should be set in the outer loop + //mesh->next = (mesh_asset*) ((uint8_t*) mesh + mesh->asset_size); + // FIXME: copy vertices, normals, tex coords, and xform + + return mesh; +} + +render_asset* +getNextFreeRenderAsset(asset_memory_block* block) +{ + return (render_asset*) + getAddressOffset(block->tail, block->tail->total_size); +} + +#include +render_asset* +initRenderAsset( + render_asset* ra, + uint total_size, + uint num_meshes, + const char* filepath) +{ + ra->total_size = total_size; + ra->num_meshes = num_meshes; + ra->name_len = (uint) std::strlen(filepath) + 1; + ra->next = nullptr; + ra->filepath = (char*) ra + sizeof(render_asset); + std::memcpy(ra->filepath, filepath, ra->name_len); + ra->head_mesh = (mesh_asset*) ((uint8_t*) ra->filepath + ra->name_len); + // TODO: texture + //ra->diffuse_texture = (util_image*) ra->filepath; + + return ra; +} + +asset_memory_block* +memInitBlock(size_t initial_size) +{ + // NOTE: need to alloc as uint8_t and recast here so we can get the correct + // size in bytes + asset_memory_block* block = (asset_memory_block*) UTIL_ALLOC( + sizeof(asset_memory_block) + initial_size, uint8_t); + block->max_size = initial_size - sizeof(asset_memory_block); + block->head = block->tail = + (render_asset*) ((uint8_t*) block + sizeof(asset_memory_block)); + return block; +} + +void +memFreeBlock(asset_memory_block* block) +{ + utilSafeFree(block); +} + +render_asset* +memPushAsset(asset_memory_block* block, const char* filepath) +{ + // TODO: rewrite mesh.cpp to load assimp structure into render_asset + // directly + mesh_group mg; + meLoadFromFile(mg, filepath); + uint name_len = (uint) std::strlen(filepath) + 1; + assert(name_len <= MAX_PATH_SIZE); + uint asize = getAssetSize(mg, name_len); + + if (getBlockFreeSpace(block) < asize) { + LOG(Error) << "not enough free space in block\n"; + return nullptr; + } + + render_asset* ra = getNextFreeRenderAsset(block); + initRenderAsset(ra, asize, mg.num_meshes, filepath); + block->max_size -= asize; + block->asset_count++; + + // NOTE: special case when adding the first asset + if (block->head == block->tail) + block->head->next = ra; + else + block->tail->next = ra; + + block->tail = ra; + mesh_asset* next = ra->head_mesh; + + for (uint i = 0; i < mg.num_meshes; i++) { + mesh_asset* mesh = initMeshAsset(next, mg.meshes[i]); + next = (mesh_asset*) getAddressOffset(mesh, mesh->asset_size); + // NOTE: leave the last link as nullptr + (i < mg.num_meshes - 1) ? mesh->next = next : mesh->next = nullptr; + } + + return ra; +} +// end WIP + render_state* renInit(const char* title, glm::vec2 viewport_dims, Uint32 SDL_init_flags) { @@ -77,6 +217,8 @@ renShutdown(render_state* rs) lightsOut(rs->lights); utilSafeFree(rs->render_groups); + memFreeBlock(rs->assets); + rs->assets = nullptr; meShutdownAssimp(); SDL_GL_DeleteContext(rs->handles->glContext); SDL_DestroyWindow(rs->handles->window);