diff --git a/src/DTRendererAsset.cpp b/src/DTRendererAsset.cpp index 5d96fef..fd97dd7 100644 --- a/src/DTRendererAsset.cpp +++ b/src/DTRendererAsset.cpp @@ -32,7 +32,7 @@ FILE_SCOPE void AssetDqnArrayMemAPICallback(DqnMemAPICallbackInfo info, DqnMemAP { case DqnMemAPICallbackType_Alloc: { - void *ptr = DqnMemStack_Push(stack, info.requestSize); + void *ptr = DqnMemStack_Push(stack, info.requestSize); result->newMemPtr = ptr; result->type = DqnMemAPICallbackType_Alloc; } @@ -89,18 +89,18 @@ FILE_SCOPE void AssetDqnArrayMemAPICallback(DqnMemAPICallbackInfo info, DqnMemAP stack->block->used -= info.oldSize; } - // NOTE(doyle): This leaves chunks of memory dead!!! But, we use - // this in our custom allocator, which its strategy is to load all - // the data, using as much re-allocations as required then after the - // fact, recompact the data by Memcopying the data together and free - // the extraneous blocks we've made. - // Otherwise, not enough space or, allocation is not the last // allocated, so can't expand inplace. DqnMemStackBlock *newBlock = DqnMemStack_AllocateCompatibleBlock(stack, info.newRequestSize); if (DqnMemStack_AttachBlock(stack, newBlock)) { + // NOTE(doyle): This leaves chunks of memory dead!!! But, we use + // this in our custom allocator, which its strategy is to load all + // the data, using as much re-allocations as required then after the + // fact, recompact the data by Memcopying the data together and free + // the extraneous blocks we've made. + void *newPtr = DqnMemStack_Push(stack, info.newRequestSize); MemcopyInternal((u8 *)newPtr, (u8 *)info.oldMemPtr, info.oldSize); @@ -118,7 +118,7 @@ FILE_SCOPE void AssetDqnArrayMemAPICallback(DqnMemAPICallbackInfo info, DqnMemAP FILE_SCOPE bool WavefModelInit(DTRWavefModel *const obj, DqnMemAPI memAPI = DqnMemAPI_DefaultUseCalloc(), - const i32 vertexInitCapacity = 100, const i32 faceInitCapacity = 100) + const i32 vertexInitCapacity = 1000, const i32 faceInitCapacity = 1000) { if (!obj) return false; @@ -144,9 +144,9 @@ FILE_SCOPE inline DTRWavefModelFace WavefModelFaceInit(i32 capacity = 3, DqnMemAPI memAPI = DqnMemAPI_DefaultUseCalloc()) { DTRWavefModelFace result = {}; - DQN_ASSERT(DqnArray_Init(&result.vertexIndexArray, capacity)); - DQN_ASSERT(DqnArray_Init(&result.textureIndexArray, capacity)); - DQN_ASSERT(DqnArray_Init(&result.normalIndexArray, capacity)); + DQN_ASSERT(DqnArray_Init(&result.vertexIndexArray, capacity, memAPI)); + DQN_ASSERT(DqnArray_Init(&result.textureIndexArray, capacity, memAPI)); + DQN_ASSERT(DqnArray_Init(&result.normalIndexArray, capacity, memAPI)); return result; } @@ -161,10 +161,12 @@ bool DTRAsset_WavefModelLoad(const PlatformAPI api, PlatformMemory *const memory return false; // TODO(doyle): Logging // TODO(doyle): Make arrays use given memory not malloc - DqnTempMemStack tmpMemRegion = DqnMemStack_BeginTempRegion(&memory->tempStack); - u8 *rawBytes = (u8 *)DqnMemStack_Push(&memory->tempStack, file.size); - size_t bytesRead = api.FileRead(&file, rawBytes, file.size); - size_t fileSize = file.size; + DqnTempMemStack tmpMemRegion = DqnMemStack_BeginTempRegion(&memory->tempStack); + DqnTempMemStack tmpAssetRegion = DqnMemStack_BeginTempRegion(&memory->assetStack); + + u8 *rawBytes = (u8 *)DqnMemStack_Push(&memory->tempStack, file.size); + size_t bytesRead = api.FileRead(&file, rawBytes, file.size); + size_t fileSize = file.size; api.FileClose(&file); if (bytesRead != file.size) { @@ -184,7 +186,7 @@ bool DTRAsset_WavefModelLoad(const PlatformAPI api, PlatformMemory *const memory memAPI.callback = AssetDqnArrayMemAPICallback; memAPI.userContext = &memory->assetStack; - if (!WavefModelInit(obj)) + if (!WavefModelInit(obj, memAPI)) { DqnMemStack_EndTempRegion(tmpMemRegion); return false; @@ -307,7 +309,7 @@ bool DTRAsset_WavefModelLoad(const PlatformAPI api, PlatformMemory *const memory while (scan && (*scan == ' ' || *scan == '\n')) scan++; if (!scan) continue; - DTRWavefModelFace face = WavefModelFaceInit(); + DTRWavefModelFace face = WavefModelFaceInit(3, memAPI); i32 numVertexesParsed = 0; bool moreVertexesToParse = true; while (moreVertexesToParse) @@ -425,8 +427,8 @@ bool DTRAsset_WavefModelLoad(const PlatformAPI api, PlatformMemory *const memory scan++; } - i32 numLen = (i32)((size_t)scan - (size_t)numStartPtr); - i32 groupSmoothing = (i32)Dqn_StrToI64(numStartPtr, numLen); + i32 numLen = (i32)((size_t)scan - (size_t)numStartPtr); + i32 groupSmoothing = (i32)Dqn_StrToI64(numStartPtr, numLen); obj->groupSmoothing = groupSmoothing; } @@ -455,9 +457,121 @@ bool DTRAsset_WavefModelLoad(const PlatformAPI api, PlatformMemory *const memory } } - DqnMemStack_EndTempRegion(tmpMemRegion); + //////////////////////////////////////////////////////////////////////////// + // Recompact Allocations + //////////////////////////////////////////////////////////////////////////// + u8 *geometryPtr = (u8 *)obj->geometryArray.data; + u8 *texturePtr = (u8 *)obj->textureArray.data; + u8 *normalPtr = (u8 *)obj->normalArray.data; - return true; + size_t geometrySize = sizeof(obj->geometryArray.data[0]) * obj->geometryArray.count; + size_t textureSize = sizeof(obj->textureArray.data[0]) * obj->textureArray.count; + size_t normalSize = sizeof(obj->normalArray.data[0]) * obj->normalArray.count; + + u8 *facePtr = (u8 *)obj->faces.data; + size_t faceSize = sizeof(obj->faces.data[0]) * obj->faces.count; + + size_t totalModelSize = geometrySize + textureSize + normalSize + faceSize; + + size_t vertIndexItemSize = sizeof(obj->faces.data[0].vertexIndexArray.data[0]); + size_t texIndexItemSize = sizeof(obj->faces.data[0].textureIndexArray.data[0]); + size_t normIndexItemSize = sizeof(obj->faces.data[0].normalIndexArray.data[0]); + { + for (i32 i = 0; i < obj->faces.count; i++) + { + DTRWavefModelFace *face = &obj->faces.data[i]; + + size_t vertIndexSize = face->vertexIndexArray.count * vertIndexItemSize; + size_t texIndexSize = face->textureIndexArray.count * texIndexItemSize; + size_t normIndexSize = face->normalIndexArray.count * normIndexItemSize; + + totalModelSize += (vertIndexSize + texIndexSize + normIndexSize); + } + } + + // IMPORTANT(doyle): We always allocate a new block, so each assets owns + // their own memory block. + DqnMemStackBlock *modelBlock = + DqnMemStack_AllocateCompatibleBlock(&memory->assetStack, totalModelSize); + if (modelBlock) + { + if (DqnMemStack_AttachBlock(&memory->assetStack, modelBlock)) + { + u8 *newGeometryPtr = (u8 *)DqnMemStack_Push(&memory->assetStack, geometrySize); + u8 *newTexturePtr = (u8 *)DqnMemStack_Push(&memory->assetStack, textureSize); + u8 *newNormalPtr = (u8 *)DqnMemStack_Push(&memory->assetStack, normalSize); + u8 *newFacePtr = (u8 *)DqnMemStack_Push(&memory->assetStack, faceSize); + MemcopyInternal(newGeometryPtr, geometryPtr, geometrySize); + MemcopyInternal(newTexturePtr, texturePtr, textureSize); + MemcopyInternal(newNormalPtr, normalPtr, normalSize); + MemcopyInternal(newFacePtr, facePtr, faceSize); + obj->geometryArray.data = (DqnV4 *)newGeometryPtr; + obj->normalArray.data = (DqnV3 *)newNormalPtr; + obj->textureArray.data = (DqnV3 *)newTexturePtr; + obj->faces.data = (DTRWavefModelFace *)newFacePtr; + + obj->geometryArray.capacity = obj->geometryArray.count; + obj->normalArray.capacity = obj->normalArray.count; + obj->textureArray.capacity = obj->textureArray.count; + obj->faces.capacity = obj->faces.count; + + for (i32 i = 0; i < obj->faces.count; i++) + { + DTRWavefModelFace *face = &obj->faces.data[i]; + size_t vertIndexSize = face->vertexIndexArray.count * vertIndexItemSize; + size_t texIndexSize = face->textureIndexArray.count * texIndexItemSize; + size_t normIndexSize = face->normalIndexArray.count * normIndexItemSize; + + u8 *newVertIndexPtr = (u8 *)DqnMemStack_Push(&memory->assetStack, vertIndexSize); + u8 *newTexIndexPtr = (u8 *)DqnMemStack_Push(&memory->assetStack, texIndexSize); + u8 *newNormIndexPtr = (u8 *)DqnMemStack_Push(&memory->assetStack, normIndexSize); + + MemcopyInternal(newVertIndexPtr, (u8 *)face->vertexIndexArray.data, vertIndexSize); + MemcopyInternal(newTexIndexPtr, (u8 *)face->textureIndexArray.data, texIndexSize); + MemcopyInternal(newNormIndexPtr, (u8 *)face->normalIndexArray.data, normIndexSize); + + face->vertexIndexArray.data = (i32 *)newVertIndexPtr; + face->textureIndexArray.data = (i32 *)newTexIndexPtr; + face->normalIndexArray.data = (i32 *)newNormIndexPtr; + + face->vertexIndexArray.capacity = face->vertexIndexArray.count; + face->textureIndexArray.capacity = face->textureIndexArray.count; + face->normalIndexArray.capacity = face->normalIndexArray.count; + } + + // NOTE: Detach the block, because the stack is in a temp region. + // End the temp region and reattach the compact model block. + DqnMemStack_DetachBlock(&memory->assetStack, modelBlock); + } + else + { + // TODO(doyle): Stack can't attach block, i.e. invalid args or + // stack is configured to be nonexpandable + DQN_ASSERT(DQN_INVALID_CODE_PATH); + } + } + else + { + // TODO(doyle): Out of memory error + DQN_ASSERT(DQN_INVALID_CODE_PATH); + } + + DqnMemStack_EndTempRegion(tmpMemRegion); + DqnMemStack_EndTempRegion(tmpAssetRegion); + DqnMemStack_ClearCurrBlock(&memory->assetStack, true); + if (modelBlock) + { + DqnMemStackBlock *firstBlock = memory->assetStack.block; + DqnMemStack_DetachBlock(&memory->assetStack, memory->assetStack.block); + + DqnMemStack_AttachBlock(&memory->assetStack, modelBlock); + DqnMemStack_AttachBlock(&memory->assetStack, firstBlock); + return true; + } + else + { + return false; + } } bool DTRAsset_FontToBitmapLoad(const PlatformAPI api, PlatformMemory *const memory, @@ -605,7 +719,6 @@ FILE_SCOPE void *STBImageMalloc(size_t size) #define STB_IMAGE_IMPLEMENTATION #include "external/stb_image.h" -// TODO(doyle): Uses malloc bool DTRAsset_BitmapLoad(const PlatformAPI api, DqnMemStack *const bitmapMemStack, DqnMemStack *const tempStack, DTRBitmap *bitmap, const char *const path) diff --git a/src/DTRendererPlatform.h b/src/DTRendererPlatform.h index 5be109d..c169278 100644 --- a/src/DTRendererPlatform.h +++ b/src/DTRendererPlatform.h @@ -128,9 +128,15 @@ typedef struct PlatformInput typedef struct PlatformMemory { - DqnMemStack mainStack; - DqnMemStack tempStack; - DqnMemStack assetStack; + union { + struct + { + DqnMemStack mainStack; + DqnMemStack tempStack; + DqnMemStack assetStack; + }; + DqnMemStack stacks[3]; + }; bool isInit; void *context; } PlatformMemory; diff --git a/src/Win32DTRenderer.cpp b/src/Win32DTRenderer.cpp index 1f1ffbf..9f71d8b 100644 --- a/src/Win32DTRenderer.cpp +++ b/src/Win32DTRenderer.cpp @@ -273,21 +273,23 @@ FILE_SCOPE void Win32HandleMenuMessages(HWND window, MSG msg, case Win32Menu_FileFlushMemory: { + for (i32 i = 0; i < DQN_ARRAY_COUNT(globalPlatformMemory.stacks); i++) + { + while (globalPlatformMemory.stacks[i].block->prevBlock) + DqnMemStack_FreeLastBlock(&globalPlatformMemory.stacks[i]); + + DqnMemStack_ClearCurrBlock(&globalPlatformMemory.stacks[i], true); + } + DqnMemStack mainStack = globalPlatformMemory.mainStack; - DqnMemStack tempStack = globalPlatformMemory.tempStack; - while (mainStack.block->prevBlock) - DqnMemStack_FreeLastBlock(&mainStack); + DqnMemStack assetStack = globalPlatformMemory.assetStack; + DqnMemStack tempStack = globalPlatformMemory.tempStack; - while (tempStack.block->prevBlock) - DqnMemStack_FreeLastBlock(&tempStack); - - DqnMemStack_ClearCurrBlock(&tempStack, true); - DqnMemStack_ClearCurrBlock(&mainStack, true); - - PlatformMemory empty = {}; - globalPlatformMemory = empty; + PlatformMemory empty = {}; + globalPlatformMemory = empty; globalPlatformMemory.mainStack = mainStack; - globalPlatformMemory.tempStack = tempStack; + globalPlatformMemory.assetStack = assetStack; + globalPlatformMemory.tempStack = tempStack; } break; diff --git a/src/dqn.h b/src/dqn.h index 07677a4..9478203 100644 --- a/src/dqn.h +++ b/src/dqn.h @@ -160,6 +160,7 @@ typedef struct DqnMemStackBlock // allowed to have new blocks. DQN_FILE_SCOPE DqnMemStackBlock *DqnMemStack_AllocateCompatibleBlock(const DqnMemStack *const stack, size_t size); DQN_FILE_SCOPE bool DqnMemStack_AttachBlock (DqnMemStack *const stack, DqnMemStackBlock *const newBlock); +DQN_FILE_SCOPE bool DqnMemStack_DetachBlock (DqnMemStack *const stack, DqnMemStackBlock *const detachBlock); // (IMPORTANT) Should only be used to free blocks that haven't been attached! // Attached blocks should be freed using FreeStackBlock(). @@ -1390,7 +1391,7 @@ DqnMemStack_AllocateCompatibleBlock(const DqnMemStack *const stack, size_t size) } DQN_FILE_SCOPE bool DqnMemStack_AttachBlock(DqnMemStack *const stack, - DqnMemStackBlock *const newBlock) + DqnMemStackBlock *const newBlock) { if (!stack || !newBlock) return false; if (stack->flags & DqnMemStackFlag_IsFixedMemoryFromUser) return false; @@ -1401,6 +1402,31 @@ DQN_FILE_SCOPE bool DqnMemStack_AttachBlock(DqnMemStack *const stack, return true; } +DQN_FILE_SCOPE bool DqnMemStack_DetachBlock(DqnMemStack *const stack, + DqnMemStackBlock *const detachBlock) +{ + if (!stack || !detachBlock) return false; + if (stack->flags & DqnMemStackFlag_IsFixedMemoryFromUser) return false; + if (stack->flags & DqnMemStackFlag_IsNotExpandable) return false; + + DqnMemStackBlock **blockPtr = &stack->block; + while (*blockPtr && *blockPtr != detachBlock) + blockPtr = &((*blockPtr)->prevBlock); + + if (*blockPtr) + { + *blockPtr = detachBlock->prevBlock; + detachBlock->prevBlock = NULL; + } + else + { + return false; + } + + return true; +} + + DQN_FILE_SCOPE void DqnMemStack_FreeBlock(DqnMemStackBlock *block) { if (!block) return;