Add asset array memory api stubs

This commit is contained in:
Doyle Thai 2017-05-26 13:31:01 +10:00
parent f89b3ff706
commit 96ac39724d
6 changed files with 190 additions and 75 deletions

View File

@ -962,33 +962,35 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer,
DTRAsset_InitGlobalState();
memory->isInit = true;
memory->context = DqnMemStack_Push(&memory->permMemStack, sizeof(DTRState));
DQN_ASSERT(memory->context);
memory->context = DqnMemStack_Push(&memory->mainStack, sizeof(DTRState));
DqnMemStack *const assetStack = &memory->assetStack;
DqnMemStack *const tempStack = &memory->tempStack;
DQN_ASSERT(memory->context);
state = (DTRState *)memory->context;
DTRAsset_FontToBitmapLoad(input->api, memory, &state->font, "Roboto-bold.ttf",
DqnV2i_2i(256, 256), DqnV2i_2i(' ', '~'), 12);
DTRAsset_BitmapLoad(input->api, &memory->permMemStack,
&memory->transMemStack, &state->bitmap, "tree00.bmp");
DTRAsset_BitmapLoad(input->api, assetStack,
tempStack, &state->bitmap, "tree00.bmp");
if (DTR_DEBUG)
{
DTRBitmap test = {};
DqnTempMemStack tmp = DqnMemStack_BeginTempRegion(&memory->transMemStack);
DTRAsset_BitmapLoad(input->api, &memory->permMemStack, &memory->transMemStack, &test, "byte_read_check.bmp");
DqnTempMemStack tmp = DqnMemStack_BeginTempRegion(&memory->tempStack);
DTRAsset_BitmapLoad(input->api, assetStack, &memory->tempStack, &test, "byte_read_check.bmp");
DqnMemStack_EndTempRegion(tmp);
}
if (DTRAsset_WavefModelLoad(input->api, memory, &state->obj, "african_head.obj"))
{
DTRAsset_BitmapLoad(input->api, &memory->permMemStack, &memory->transMemStack,
&state->objTex, "african_head_diffuse.tga");
DTRAsset_BitmapLoad(input->api, assetStack, tempStack, &state->objTex,
"african_head_diffuse.tga");
}
DTRDebug_TestWavefFaceAndVertexParser(&state->obj);
}
DqnTempMemStack transMemTmpRegion = DqnMemStack_BeginTempRegion(&memory->transMemStack);
DqnTempMemStack transMemTmpRegion = DqnMemStack_BeginTempRegion(&memory->tempStack);
DTRRenderBuffer renderBuffer = {};
renderBuffer.width = platformRenderBuffer->width;
@ -997,7 +999,7 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer,
renderBuffer.memory = (u8 *)platformRenderBuffer->memory;
u32 zBufferSize = platformRenderBuffer->width * platformRenderBuffer->height;
renderBuffer.zBuffer = (f32 *)DqnMemStack_Push(&memory->transMemStack,
renderBuffer.zBuffer = (f32 *)DqnMemStack_Push(&memory->tempStack,
zBufferSize * sizeof(*renderBuffer.zBuffer));
for (u32 i = 0; i < zBufferSize; i++) renderBuffer.zBuffer[i] = DQN_F32_MIN;
@ -1153,8 +1155,8 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer,
// End Update
////////////////////////////////////////////////////////////////////////////
DqnMemStack_EndTempRegion(transMemTmpRegion);
DqnMemStack_ClearCurrBlock(&memory->transMemStack, true);
DqnMemStack_ClearCurrBlock(&memory->tempStack, true);
DQN_ASSERT(memory->transMemStack.tempStackCount == 0);
DQN_ASSERT(memory->permMemStack.tempStackCount == 0);
DQN_ASSERT(memory->tempStack.tempStackCount == 0);
DQN_ASSERT(memory->mainStack.tempStackCount == 0);
}

View File

@ -17,17 +17,117 @@ void DTRAsset_InitGlobalState()
stbi_set_flip_vertically_on_load(true);
}
FILE_SCOPE bool WavefModelInit(DTRWavefModel *const obj, const i32 vertexInitCapacity = 100,
const i32 faceInitCapacity = 100)
FILE_SCOPE void MemcopyInternal(u8 *dest, u8 *src, size_t numBytes)
{
if (!dest || !src || numBytes == 0) return;
for (size_t i = 0; i < numBytes; i++)
dest[i] = src[i];
}
FILE_SCOPE void AssetDqnArrayMemAPICallback(DqnMemAPICallbackInfo info, DqnMemAPICallbackResult *result)
{
DQN_ASSERT(info.type != DqnMemAPICallbackType_Invalid);
DqnMemStack *stack = static_cast<DqnMemStack *>(info.userContext);
switch (info.type)
{
case DqnMemAPICallbackType_Alloc:
{
void *ptr = DqnMemStack_Push(stack, info.requestSize);
result->newMemPtr = ptr;
result->type = DqnMemAPICallbackType_Alloc;
}
break;
case DqnMemAPICallbackType_Free:
{
DqnMemStackBlock **blockPtr = &stack->block;
while (*blockPtr && (*blockPtr)->memory != info.ptrToFree)
{
// NOTE(doyle): Ensure that the base ptr of each block is always
// actually aligned so we don't ever miss finding the block if
// the allocator had to realign the pointer from the base
// address.
if (DTR_DEBUG)
{
size_t memBaseAddr = (size_t)((*blockPtr)->memory);
DQN_ASSERT(DQN_ALIGN_POW_N(memBaseAddr, stack->byteAlign) ==
memBaseAddr);
}
blockPtr = &((*blockPtr)->prevBlock);
}
DQN_ASSERT(*blockPtr && (*blockPtr)->memory == info.ptrToFree);
DqnMemStackBlock *blockToFree = *blockPtr;
*blockPtr = blockToFree->prevBlock;
DqnMem_Free(blockToFree);
}
break;
case DqnMemAPICallbackType_Realloc:
{
result->type = DqnMemAPICallbackType_Realloc;
// Check if the ptr is the last thing that was allocated. If so we
// can check if there's enough space in place for realloc and give
// them that.
u8 *currMemPtr = (u8 *)(stack->block->memory + stack->block->used);
u8 *checkPtr = currMemPtr - info.oldSize;
if (checkPtr == info.oldMemPtr)
{
if ((stack->block->used + info.newRequestSize) < stack->block->size)
{
stack->block->used += info.newRequestSize;
result->newMemPtr = info.oldMemPtr;
return;
}
// The allocation was the last one allocated, but there's not
// enough space to realloc in the block. For book-keeping,
// "deallocate" the old mem ptr by reverting the usage of the
// memory stack.
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))
{
void *newPtr = DqnMemStack_Push(stack, info.newRequestSize);
MemcopyInternal((u8 *)newPtr, (u8 *)info.oldMemPtr, info.oldSize);
result->newMemPtr = newPtr;
}
else
{
// TODO(doyle): Die out of memory
DQN_ASSERT(DQN_INVALID_CODE_PATH);
}
}
break;
}
}
FILE_SCOPE bool WavefModelInit(DTRWavefModel *const obj,
DqnMemAPI memAPI = DqnMemAPI_DefaultUseCalloc(),
const i32 vertexInitCapacity = 100, const i32 faceInitCapacity = 100)
{
if (!obj) return false;
bool initialised = false;
initialised |= DqnArray_Init(&obj->geometryArray, vertexInitCapacity);
initialised |= DqnArray_Init(&obj->textureArray, vertexInitCapacity);
initialised |= DqnArray_Init(&obj->normalArray, vertexInitCapacity);
initialised |= DqnArray_Init(&obj->faces, faceInitCapacity);
initialised |= DqnArray_Init(&obj->geometryArray, vertexInitCapacity, memAPI);
initialised |= DqnArray_Init(&obj->textureArray, vertexInitCapacity, memAPI);
initialised |= DqnArray_Init(&obj->normalArray, vertexInitCapacity, memAPI);
initialised |= DqnArray_Init(&obj->faces, faceInitCapacity, memAPI);
if (!initialised)
{
@ -40,7 +140,8 @@ FILE_SCOPE bool WavefModelInit(DTRWavefModel *const obj, const i32 vertexInitCap
return initialised;
}
FILE_SCOPE inline DTRWavefModelFace WavefModelFaceInit(i32 capacity = 3)
FILE_SCOPE inline DTRWavefModelFace
WavefModelFaceInit(i32 capacity = 3, DqnMemAPI memAPI = DqnMemAPI_DefaultUseCalloc())
{
DTRWavefModelFace result = {};
DQN_ASSERT(DqnArray_Init(&result.vertexIndexArray, capacity));
@ -60,8 +161,8 @@ 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->transMemStack);
u8 *rawBytes = (u8 *)DqnMemStack_Push(&memory->transMemStack, file.size);
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;
api.FileClose(&file);
@ -79,6 +180,10 @@ bool DTRAsset_WavefModelLoad(const PlatformAPI api, PlatformMemory *const memory
WavefVertexType_Normal,
};
DqnMemAPI memAPI = {};
memAPI.callback = AssetDqnArrayMemAPICallback;
memAPI.userContext = &memory->assetStack;
if (!WavefModelInit(obj))
{
DqnMemStack_EndTempRegion(tmpMemRegion);
@ -223,6 +328,9 @@ bool DTRAsset_WavefModelLoad(const PlatformAPI api, PlatformMemory *const memory
// so offset by -1 to make it zero-based indexes.
i32 vertIndex = (i32)Dqn_StrToI64(numStartPtr, numLen) - 1;
// TODO(doyle): Does not supprot relative vertexes yet
DQN_ASSERT(vertIndex >= 0);
if (type == WavefVertexType_Geometric)
{
DQN_ASSERT(DqnArray_Push(&face.vertexIndexArray, vertIndex));
@ -288,7 +396,7 @@ bool DTRAsset_WavefModelLoad(const PlatformAPI api, PlatformMemory *const memory
DQN_ASSERT(!obj->groupName[obj->groupNameIndex]);
obj->groupName[obj->groupNameIndex++] = (char *)DqnMemStack_Push(
&memory->permMemStack, (nameLen + 1) * sizeof(char));
&memory->mainStack, (nameLen + 1) * sizeof(char));
for (i32 i = 0; i < nameLen; i++)
obj->groupName[obj->groupNameIndex - 1][i] = namePtr[i];
@ -370,8 +478,8 @@ bool DTRAsset_FontToBitmapLoad(const PlatformAPI api, PlatformMemory *const memo
if (!api.FileOpen(path, &file, PlatformFilePermissionFlag_Read, PlatformFileAction_OpenOnly))
return false; // TODO(doyle): Logging
DqnTempMemStack tmpMemRegion = DqnMemStack_BeginTempRegion(&memory->transMemStack);
u8 *fontBuf = (u8 *)DqnMemStack_Push(&memory->transMemStack, file.size);
DqnTempMemStack tmpMemRegion = DqnMemStack_BeginTempRegion(&memory->tempStack);
u8 *fontBuf = (u8 *)DqnMemStack_Push(&memory->tempStack, file.size);
size_t bytesRead = api.FileRead(&file, fontBuf, file.size);
api.FileClose(&file);
if (bytesRead != file.size)
@ -393,7 +501,7 @@ bool DTRAsset_FontToBitmapLoad(const PlatformAPI api, PlatformMemory *const memo
// Pack font data to bitmap
////////////////////////////////////////////////////////////////////////////
loadedFont.bitmap = (u8 *)DqnMemStack_Push(
&memory->permMemStack,
&memory->mainStack,
(size_t)(loadedFont.bitmapDim.w * loadedFont.bitmapDim.h));
stbtt_pack_context fontPackContext = {};
@ -406,7 +514,7 @@ bool DTRAsset_FontToBitmapLoad(const PlatformAPI api, PlatformMemory *const memo
(i32)((codepointRange.max + 1) - codepointRange.min);
loadedFont.atlas = (stbtt_packedchar *)DqnMemStack_Push(
&memory->permMemStack, numCodepoints * sizeof(stbtt_packedchar));
&memory->mainStack, numCodepoints * sizeof(stbtt_packedchar));
stbtt_PackFontRange(&fontPackContext, fontBuf, 0,
STBTT_POINT_SIZE(sizeInPt), (i32)codepointRange.min,
numCodepoints, loadedFont.atlas);
@ -456,13 +564,6 @@ bool DTRAsset_FontToBitmapLoad(const PlatformAPI api, PlatformMemory *const memo
// TODO(doyle): Not threadsafe
FILE_SCOPE DqnMemStack *globalSTBImageAllocator;
FILE_SCOPE void MemcopyInternal(u8 *dest, u8 *src, size_t numBytes)
{
if (!dest || !src || numBytes == 0) return;
for (size_t i = 0; i < numBytes; i++)
dest[i] = src[i];
}
FILE_SCOPE void *STBImageReallocSized(void *ptr, size_t oldSize, size_t newSize)
{
// TODO(doyle): Implement when needed. There's no easy way using our stack
@ -506,18 +607,18 @@ FILE_SCOPE void *STBImageMalloc(size_t size)
// TODO(doyle): Uses malloc
bool DTRAsset_BitmapLoad(const PlatformAPI api, DqnMemStack *const bitmapMemStack,
DqnMemStack *const transMemStack, DTRBitmap *bitmap,
DqnMemStack *const tempStack, DTRBitmap *bitmap,
const char *const path)
{
if (!bitmap || !bitmapMemStack || !transMemStack) return false;
if (!bitmap || !bitmapMemStack || !tempStack) return false;
bool result = false;
PlatformFile file = {};
if (!api.FileOpen(path, &file, PlatformFilePermissionFlag_Read, PlatformFileAction_OpenOnly))
return result;
DqnTempMemStack tmpMemRegion = DqnMemStack_BeginTempRegion(transMemStack);
u8 *const rawData = (u8 *)DqnMemStack_Push (transMemStack, file.size);
DqnTempMemStack tmpMemRegion = DqnMemStack_BeginTempRegion(tempStack);
u8 *const rawData = (u8 *)DqnMemStack_Push (tempStack, file.size);
size_t bytesRead = api.FileRead (&file, rawData, file.size);
if (bytesRead != file.size) goto cleanup;

View File

@ -46,7 +46,7 @@ void DTRDebug_TestWavefFaceAndVertexParser(DTRWavefModel *const obj)
}
}
void DTRDebug_DumpZBuffer(DTRRenderBuffer *const renderBuffer, DqnMemStack *const transMemStack)
void DTRDebug_DumpZBuffer(DTRRenderBuffer *const renderBuffer, DqnMemStack *const tempStack)
{
if (DTR_DEBUG)
{
@ -64,10 +64,10 @@ void DTRDebug_DumpZBuffer(DTRRenderBuffer *const renderBuffer, DqnMemStack *cons
}
}
DqnTempMemStack tmpMemRegion = DqnMemStack_BeginTempRegion(transMemStack);
DqnTempMemStack tmpMemRegion = DqnMemStack_BeginTempRegion(tempStack);
size_t bufSize = DQN_MEGABYTE(16);
char *bufString = (char *)DqnMemStack_Push(transMemStack, bufSize);
char *bufString = (char *)DqnMemStack_Push(tempStack, bufSize);
char *bufPtr = bufString;
for (i32 i = 0; i < renderBuffer->width * renderBuffer->height; i++)
{
@ -149,6 +149,7 @@ FILE_SCOPE void PushMemStackText(const char *const name, const DqnMemStack *cons
i32 numBlocks = 0;
DqnMemStackBlock *blockPtr = stack->block;
size_t freeSizeOfCurrBlock = (blockPtr) ? blockPtr->size - blockPtr->used : 0;
while (blockPtr)
{
totalUsed += blockPtr->used;
@ -159,7 +160,7 @@ FILE_SCOPE void PushMemStackText(const char *const name, const DqnMemStack *cons
size_t totalUsedKb = totalUsed / 1024;
size_t totalSizeKb = totalSize / 1024;
size_t totalWastedKb = totalWasted / 1024;
size_t totalWastedKb = (totalSize - totalUsed - freeSizeOfCurrBlock) / 1024;
char str[128] = {};
Dqn_sprintf(str, "%s: %d block(s): %_$lld/%_$lld: wasted: %_$lld", name, numBlocks,
@ -194,8 +195,9 @@ void DTRDebug_Update(DTRState *const state,
// memory
{
PushMemStackText("PermBuffer", &memory->permMemStack);
PushMemStackText("TransBuffer", &memory->transMemStack);
PushMemStackText("MainStack", &memory->mainStack);
PushMemStackText("TempStack", &memory->tempStack);
PushMemStackText("AssetStack", &memory->assetStack);
}
DTRDebug_PushText("Mouse: %d, %d", input->mouse.x, input->mouse.y);

View File

@ -128,8 +128,9 @@ typedef struct PlatformInput
typedef struct PlatformMemory
{
DqnMemStack permMemStack;
DqnMemStack transMemStack;
DqnMemStack mainStack;
DqnMemStack tempStack;
DqnMemStack assetStack;
bool isInit;
void *context;
} PlatformMemory;

View File

@ -273,21 +273,21 @@ FILE_SCOPE void Win32HandleMenuMessages(HWND window, MSG msg,
case Win32Menu_FileFlushMemory:
{
DqnMemStack permMemStack = globalPlatformMemory.permMemStack;
DqnMemStack transMemStack = globalPlatformMemory.transMemStack;
while (permMemStack.block->prevBlock)
DqnMemStack_FreeLastBlock(&permMemStack);
DqnMemStack mainStack = globalPlatformMemory.mainStack;
DqnMemStack tempStack = globalPlatformMemory.tempStack;
while (mainStack.block->prevBlock)
DqnMemStack_FreeLastBlock(&mainStack);
while (transMemStack.block->prevBlock)
DqnMemStack_FreeLastBlock(&transMemStack);
while (tempStack.block->prevBlock)
DqnMemStack_FreeLastBlock(&tempStack);
DqnMemStack_ClearCurrBlock(&transMemStack, true);
DqnMemStack_ClearCurrBlock(&permMemStack, true);
DqnMemStack_ClearCurrBlock(&tempStack, true);
DqnMemStack_ClearCurrBlock(&mainStack, true);
PlatformMemory empty = {};
globalPlatformMemory = empty;
globalPlatformMemory.permMemStack = permMemStack;
globalPlatformMemory.transMemStack = transMemStack;
globalPlatformMemory.mainStack = mainStack;
globalPlatformMemory.tempStack = tempStack;
}
break;
@ -546,8 +546,10 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
////////////////////////////////////////////////////////////////////////////
// Platform Data Pre-amble
////////////////////////////////////////////////////////////////////////////
DQN_ASSERT(DqnMemStack_Init(&globalPlatformMemory.permMemStack, DQN_MEGABYTE(4), true, 4) &&
DqnMemStack_Init(&globalPlatformMemory.transMemStack, DQN_MEGABYTE(4), true, 4));
DQN_ASSERT(DqnMemStack_Init(&globalPlatformMemory.mainStack, DQN_MEGABYTE(4), true, 4) &&
DqnMemStack_Init(&globalPlatformMemory.tempStack, DQN_MEGABYTE(4), true, 4) &&
DqnMemStack_Init(&globalPlatformMemory.assetStack, DQN_MEGABYTE(4), true, 4)
);
PlatformAPI platformAPI = {};
platformAPI.FileOpen = Platform_FileOpen;

View File

@ -96,24 +96,18 @@ DQN_FILE_SCOPE void DqnMem_Free (void *memory);
// BeginTempRegion and EndTempRegion functions. Specifically freeing
// individual items is typically not generalisable in this scheme.
typedef struct DqnMemStackBlock
{
u8 *memory;
size_t used;
size_t size;
DqnMemStackBlock *prevBlock;
} DqnMemStackBlock;
enum DqnMemStackFlag
{
DqnMemStackFlag_IsNotExpandable = (1 << 0),
DqnMemStackFlag_IsFixedMemoryFromUser = (1 << 1), // NOTE(doyle): Required to indicate we CAN'T free this memory when free is called.
};
////////////////////////////////////////////////////////////////////////////////
// Advanced API Structs
////////////////////////////////////////////////////////////////////////////////
typedef struct DqnMemStack
{
DqnMemStackBlock *block;
struct DqnMemStackBlock *block;
u32 flags;
i32 tempStackCount;
@ -122,8 +116,8 @@ typedef struct DqnMemStack
typedef struct DqnTempMemStack
{
DqnMemStack *stack;
DqnMemStackBlock *startingBlock;
DqnMemStack *stack;
struct DqnMemStackBlock *startingBlock;
size_t used;
} DqnTempMemStack;
@ -132,12 +126,12 @@ DQN_FILE_SCOPE bool DqnMemStack_InitWithFixedMem (DqnMemStack *const stack, u8 *
DQN_FILE_SCOPE bool DqnMemStack_InitWithFixedSize(DqnMemStack *const stack, size_t size, const bool zeroClear, const u32 byteAlign = 4); // Single allocation from platform, no further allocations, returns NULL of allocate if out of space
DQN_FILE_SCOPE bool DqnMemStack_Init (DqnMemStack *const stack, size_t size, const bool zeroClear, const u32 byteAlign = 4); // Allocates from platform dynamically as space runs out
DQN_FILE_SCOPE void *DqnMemStack_Push (DqnMemStack *const stack, size_t size); // Returns NULL if out of space and stack is using fixed memory/size, or platform allocation fails
DQN_FILE_SCOPE bool DqnMemStack_Pop (DqnMemStack *const stack, void *ptr, size_t size); // Frees the given ptr. It MUST be the last allocated item in the stack
DQN_FILE_SCOPE void DqnMemStack_Free (DqnMemStack *const stack); // Frees all blocks belonging to this stack
DQN_FILE_SCOPE bool DqnMemStack_FreeStackBlock(DqnMemStack *const stack, DqnMemStackBlock *block); // Frees the specified block, returns false if block doesn't belong
DQN_FILE_SCOPE void *DqnMemStack_Push (DqnMemStack *const stack, size_t size); // Returns NULL if out of space and stack is using fixed memory/size, or platform allocation fails.
DQN_FILE_SCOPE bool DqnMemStack_Pop (DqnMemStack *const stack, void *ptr, size_t size); // Frees the given ptr. It MUST be the last allocated item in the stack.
DQN_FILE_SCOPE void DqnMemStack_Free (DqnMemStack *const stack); // Frees all blocks belonging to this stack.
DQN_FILE_SCOPE bool DqnMemStack_FreeStackBlock(DqnMemStack *const stack, DqnMemStackBlock *block); // Frees the specified block, returns false if block doesn't belong, calls DqnMem_Free().
DQN_FILE_SCOPE bool DqnMemStack_FreeLastBlock (DqnMemStack *const stack); // Frees the last-most memory block. If last block, free that block, next allocate will attach a block.
DQN_FILE_SCOPE void DqnMemStack_ClearCurrBlock(DqnMemStack *const stack, const bool zeroClear); // Reset the current memory block usage to 0
DQN_FILE_SCOPE void DqnMemStack_ClearCurrBlock(DqnMemStack *const stack, const bool zeroClear); // Reset the current memory block usage to 0.
// TempMemStack is only required for the function. Once BeginTempRegion() is called, subsequent allocation calls can be made using the original stack.
// Upon EndTempRegion() the original stack will free any additional blocks it allocated during the temp region and revert to the original
@ -147,7 +141,20 @@ DQN_FILE_SCOPE void DqnMemStack_ClearCurrBlock(DqnMemStack *const stack, const
DQN_FILE_SCOPE DqnTempMemStack DqnMemStack_BeginTempRegion(DqnMemStack *const stack);
DQN_FILE_SCOPE void DqnMemStack_EndTempRegion (DqnTempMemStack tempstack);
////////////////////////////////////////////////////////////////////////////////
// (OPTIONAL) DqnMemStack Advanced API
// Blocks are freely modifiable if you want fine grained control. Size value and
// memory ptr should _NOT_ be modified directly, only indirectly through the
// regular API.
typedef struct DqnMemStackBlock
{
u8 *memory;
size_t size;
size_t used;
DqnMemStackBlock *prevBlock;
} DqnMemStackBlock;
// This is useful for forcing a new block to be used. AllocateCompatibleBlock
// will fail if the supplied stack has flags set such that the stack is not
// allowed to have new blocks.