diff --git a/src/DRenderer.cpp b/src/DRenderer.cpp index abf12d6..23bd256 100644 --- a/src/DRenderer.cpp +++ b/src/DRenderer.cpp @@ -28,11 +28,27 @@ typedef struct DRFont stbtt_packedchar *atlas; } DRFont; +typedef struct DRBitmap +{ + u8 *memory; + DqnV2i dim; + i32 bytesPerPixel; +} DRBitmap; + typedef struct DRState { - DRFont font; + DRFont font; + DRBitmap bitmap; } DRState; +typedef struct DRDebug +{ + i32 setPixelsPerFrame; + i32 totalSetPixels; + + i32 displayYOffset; +} DRDebug; + FILE_SCOPE inline DqnV4 PreMultiplyAlpha(DqnV4 color) { DqnV4 result; @@ -45,14 +61,15 @@ FILE_SCOPE inline DqnV4 PreMultiplyAlpha(DqnV4 color) return result; } +FILE_SCOPE DRDebug globalDebug; + // IMPORTANT(doyle): Color is expected to be premultiplied already FILE_SCOPE inline void SetPixel(PlatformRenderBuffer *const renderBuffer, const i32 x, const i32 y, const DqnV4 color) { - if (!renderBuffer) return; - if (x < 0 || x > renderBuffer->width - 1) return; - if (y < 0 || y > renderBuffer->height - 1) return; + if (x <= 0 || x > (renderBuffer->width - 1)) return; + if (y <= 0 || y > (renderBuffer->height - 1)) return; u32 *const bitmapPtr = (u32 *)renderBuffer->memory; const u32 pitchInU32 = (renderBuffer->width * renderBuffer->bytesPerPixel) / 4; @@ -85,6 +102,8 @@ FILE_SCOPE inline void SetPixel(PlatformRenderBuffer *const renderBuffer, (u32)(destG) << 8 | (u32)(destB) << 0); bitmapPtr[x + (y * pitchInU32)] = pixel; + + globalDebug.setPixelsPerFrame++; } FILE_SCOPE void DrawLine(PlatformRenderBuffer *const renderBuffer, DqnV2i a, @@ -507,6 +526,165 @@ FILE_SCOPE void BitmapFontCreate(const PlatformAPI api, DqnMemBuffer_EndTempRegion(transientTempBufferRegion); } +FILE_SCOPE void DrawBitmap(PlatformRenderBuffer *const renderBuffer, + DRBitmap *const bitmap, i32 x, i32 y) +{ + if (!bitmap || !bitmap->memory) return; + + const i32 pitch = bitmap->dim.w * bitmap->bytesPerPixel; + for (i32 bitmapY = 0; bitmapY < bitmap->dim.w; bitmapY++) + { + u8 *const srcRow = bitmap->memory + (bitmapY * pitch); + i32 bufferY = y + bitmapY; + + for (i32 bitmapX = 0; bitmapX < bitmap->dim.w; bitmapX++) + { + u32 *pixelPtr = (u32 *)srcRow; + u32 pixel = pixelPtr[bitmapX]; + i32 bufferX = x + bitmapX; + + DqnV4 color = {}; + color.a = (f32)(pixel >> 24); + color.b = (f32)((pixel >> 16) & 0xFF); + color.g = (f32)((pixel >> 8) & 0xFF); + color.r = (f32)((pixel >> 0) & 0xFF); + + SetPixel(renderBuffer, bufferX, bufferY, color); + } + } +} + +FILE_SCOPE bool BitmapLoad(const PlatformAPI api, DRBitmap *bitmap, + const char *const path, + DqnMemBuffer *const transientBuffer) +{ + if (!bitmap) return false; + + PlatformFile file = {}; + if (!api.FileOpen(path, &file, PlatformFilePermissionFlag_Read)) + return false; + + DqnTempBuffer tempBuffer = DqnMemBuffer_BeginTempRegion(transientBuffer); + { + u8 *const rawData = + (u8 *)DqnMemBuffer_Allocate(transientBuffer, file.size); + size_t bytesRead = api.FileRead(&file, rawData, file.size); + api.FileClose(&file); + + if (bytesRead != file.size) + { + DqnMemBuffer_EndTempRegion(tempBuffer); + return false; + } + + bitmap->memory = + stbi_load_from_memory(rawData, (i32)file.size, &bitmap->dim.w, + &bitmap->dim.h, &bitmap->bytesPerPixel, 4); + } + DqnMemBuffer_EndTempRegion(tempBuffer); + if (!bitmap->memory) return false; + + const i32 pitch = bitmap->dim.w * bitmap->bytesPerPixel; + for (i32 y = 0; y < bitmap->dim.h; y++) + { + u8 *const srcRow = bitmap->memory + (y * pitch); + for (i32 x = 0; x < bitmap->dim.w; x++) + { + u32 *pixelPtr = (u32 *)srcRow; + u32 pixel = pixelPtr[x]; + + DqnV4 color = {}; + color.a = (f32)(pixel >> 24); + color.b = (f32)((pixel >> 16) & 0xFF); + color.g = (f32)((pixel >> 8) & 0xFF); + color.r = (f32)((pixel >> 0) & 0xFF); + color = PreMultiplyAlpha(color); + + pixel = (((u32)color.a << 24) | + ((u32)color.b << 16) | + ((u32)color.g << 8) | + ((u32)color.r << 0)); + + pixelPtr[x] = pixel; + } + } + + return true; +} + +void DebugDisplayMemBuffer(PlatformRenderBuffer *const renderBuffer, + const char *const name, + const DqnMemBuffer *const buffer, + DqnV2 *const debugP, const DRFont font) +{ + if (!name && !buffer && !debugP) return; + + size_t totalUsed = 0; + size_t totalSize = 0; + size_t totalWasted = 0; + i32 numBlocks = 0; + + DqnMemBufferBlock *blockPtr = buffer->block; + while (blockPtr) + { + totalUsed += blockPtr->used; + totalSize += blockPtr->size; + blockPtr = blockPtr->prevBlock; + numBlocks++; + } + + size_t totalUsedKb = totalUsed / 1024; + size_t totalSizeKb = totalSize / 1024; + size_t totalWastedKb = totalWasted / 1024; + + char str[128] = {}; + Dqn_sprintf(str, "%s: %d block(s): %dkb/%dkb", name, numBlocks, + totalUsedKb, totalSizeKb); + + DrawText(renderBuffer, font, *debugP, str); + debugP->y += globalDebug.displayYOffset; +} + +void DebugUpdate(PlatformRenderBuffer *const renderBuffer, + PlatformInput *const input, PlatformMemory *const memory, + const DRFont font) +{ + DRDebug *const debug = &globalDebug; + + globalDebug.displayYOffset = -(i32)(font.sizeInPt + 0.5f); + DQN_ASSERT(globalDebug.displayYOffset < 0); + + DqnV2 debugP = DqnV2_2i(0, renderBuffer->height + globalDebug.displayYOffset); + + debug->totalSetPixels += debug->setPixelsPerFrame; + debug->totalSetPixels = DQN_MAX(0, debug->totalSetPixels); + debug->setPixelsPerFrame = 0; + + // totalSetPixels + { + char str[128] = {}; + Dqn_sprintf(str, "%s: %d", "TotalSetPixels", debug->totalSetPixels); + DrawText(renderBuffer, font, debugP, str); + debugP.y += globalDebug.displayYOffset; + } + + // setPixelsPerFrame + { + char str[128] = {}; + Dqn_sprintf(str, "%s: %d", "SetPixelsPerFrame", debug->setPixelsPerFrame); + DrawText(renderBuffer, font, debugP, str); + debugP.y += globalDebug.displayYOffset; + } + + // memory + { + DebugDisplayMemBuffer(renderBuffer, "PermBuffer", + &memory->permanentBuffer, &debugP, font); + DebugDisplayMemBuffer(renderBuffer, "TransBuffer", + &memory->transientBuffer, &debugP, font); + } +} + extern "C" void DR_Update(PlatformRenderBuffer *const renderBuffer, PlatformInput *const input, PlatformMemory *const memory) @@ -514,17 +692,20 @@ extern "C" void DR_Update(PlatformRenderBuffer *const renderBuffer, DRState *state = (DRState *)memory->context; if (!memory->isInit) { + stbi_set_flip_vertically_on_load(true); memory->isInit = true; memory->context = DqnMemBuffer_Allocate(&memory->permanentBuffer, sizeof(DRState)); DQN_ASSERT(memory->context); state = (DRState *)memory->context; - BitmapFontCreate(input->api, memory, &state->font, "Roboto-bold.ttf", - DqnV2i_2i(256, 256), DqnV2i_2i(' ', '~'), 35); + BitmapFontCreate(input->api, memory, &state->font, "consola.ttf", + DqnV2i_2i(256, 256), DqnV2i_2i(' ', '~'), 18); + DQN_ASSERT(BitmapLoad(input->api, &state->bitmap, "lune_logo.png", + &memory->transientBuffer)); } - ClearRenderBuffer(renderBuffer, DqnV3_3f(0, 255, 0)); + ClearRenderBuffer(renderBuffer, DqnV3_3f(0, 0, 0)); DqnV4 colorRed = DqnV4_4i(50, 0, 0, 255); DqnV2i bufferMidP = DqnV2i_2f(renderBuffer->width * 0.5f, renderBuffer->height * 0.5f); i32 boundsOffset = 50; @@ -555,9 +736,6 @@ extern "C" void DR_Update(PlatformRenderBuffer *const renderBuffer, } } - DqnV2 fontP = DqnV2_2i(200, 180); - DrawText(renderBuffer, state->font, fontP, "hello world!"); - DrawTriangle(renderBuffer, t0[0], t0[1], t0[2], colorRed); DrawTriangle(renderBuffer, t1[0], t1[1], t1[2], colorRed); DrawTriangle(renderBuffer, t2[0], t2[1], t2[2], colorRed); @@ -565,4 +743,10 @@ extern "C" void DR_Update(PlatformRenderBuffer *const renderBuffer, DqnV4 colorRedHalfA = DqnV4_4i(255, 0, 0, 190); DrawTriangle(renderBuffer, t3[0], t3[1], t3[2], colorRedHalfA); + DqnV2 fontP = DqnV2_2i(200, 180); + DrawText(renderBuffer, state->font, fontP, "hello world!"); + DrawBitmap(renderBuffer, &state->bitmap, 700, 400); + + DebugUpdate(renderBuffer, input, memory, state->font); + } diff --git a/src/DRendererPlatform.h b/src/DRendererPlatform.h index f45c951..ce0ac65 100644 --- a/src/DRendererPlatform.h +++ b/src/DRendererPlatform.h @@ -70,8 +70,9 @@ typedef struct KeyState typedef struct PlatformInput { f32 deltaForFrame; - PlatformAPI api; + f64 timeNowInS; + PlatformAPI api; union { KeyState key[key_count]; struct diff --git a/src/Win32DRenderer.cpp b/src/Win32DRenderer.cpp index 6aec3ac..1573ea8 100644 --- a/src/Win32DRenderer.cpp +++ b/src/Win32DRenderer.cpp @@ -90,6 +90,7 @@ typedef struct Win32RenderBitmap } Win32RenderBitmap; FILE_SCOPE Win32RenderBitmap globalRenderBitmap; +FILE_SCOPE PlatformMemory globalPlatformMemory; FILE_SCOPE bool globalRunning; typedef struct Win32ExternalCode @@ -103,6 +104,7 @@ typedef struct Win32ExternalCode enum Win32Menu { Win32Menu_FileOpen = 4, + Win32Menu_FileFlushMemory, Win32Menu_FileExit, }; @@ -175,6 +177,7 @@ FILE_SCOPE void Win32CreateMenu(HWND window) HMENU menu = CreatePopupMenu(); AppendMenu(menuBar, MF_STRING | MF_POPUP, (UINT_PTR)menu, "File"); AppendMenu(menu, MF_STRING, Win32Menu_FileOpen, "Open"); + AppendMenu(menu, MF_STRING, Win32Menu_FileFlushMemory, "Flush Memory"); AppendMenu(menu, MF_STRING, Win32Menu_FileExit, "Exit"); } SetMenu(window, menuBar); @@ -248,6 +251,26 @@ FILE_SCOPE void Win32HandleMenuMessages(HWND window, MSG msg, } break; + case Win32Menu_FileFlushMemory: + { + DqnMemBuffer permBuffer = globalPlatformMemory.permanentBuffer; + DqnMemBuffer transBuffer = globalPlatformMemory.transientBuffer; + while (permBuffer.block->prevBlock) + DqnMemBuffer_FreeLastBlock(&permBuffer); + + while (transBuffer.block->prevBlock) + DqnMemBuffer_FreeLastBlock(&transBuffer); + + DqnMemBuffer_ClearCurrBlock(&transBuffer, true); + DqnMemBuffer_ClearCurrBlock(&permBuffer, true); + + PlatformMemory empty = {}; + globalPlatformMemory = empty; + globalPlatformMemory.permanentBuffer = permBuffer; + globalPlatformMemory.transientBuffer = transBuffer; + } + break; + case Win32Menu_FileOpen: { #if 0 @@ -475,9 +498,8 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, //////////////////////////////////////////////////////////////////////////// // Update Loop //////////////////////////////////////////////////////////////////////////// - PlatformMemory platformMemory = {}; - DQN_ASSERT(DqnMemBuffer_Init(&platformMemory.permanentBuffer, DQN_MEGABYTE(1), true, 4) && - DqnMemBuffer_Init(&platformMemory.transientBuffer, DQN_MEGABYTE(1), true, 4)); + DQN_ASSERT(DqnMemBuffer_Init(&globalPlatformMemory.permanentBuffer, DQN_MEGABYTE(1), true, 4) && + DqnMemBuffer_Init(&globalPlatformMemory.transientBuffer, DQN_MEGABYTE(1), true, 4)); const f32 TARGET_FRAMES_PER_S = 60.0f; f32 targetSecondsPerFrame = 1 / TARGET_FRAMES_PER_S; @@ -506,6 +528,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, { PlatformInput platformInput = {}; + platformInput.timeNowInS = DqnTime_NowInS(); platformInput.deltaForFrame = (f32)frameTimeInS; platformInput.api = platformAPI; Win32ProcessMessages(mainWindow, &platformInput); @@ -519,7 +542,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, if (dllCode.DR_Update) { dllCode.DR_Update(&platformBuffer, &platformInput, - &platformMemory); + &globalPlatformMemory); } } diff --git a/src/dqn.h b/src/dqn.h index a1abaa6..5d876b2 100644 --- a/src/dqn.h +++ b/src/dqn.h @@ -611,11 +611,11 @@ DQN_FILE_SCOPE i32 Dqn_strlen (const char *a); DQN_FILE_SCOPE i32 Dqn_strlenDelimitWith(const char *a, const char delimiter); DQN_FILE_SCOPE char *Dqn_strncpy (char *dest, const char *src, i32 numChars); -#define DQN_I32_TO_STR_MAX_BUF_SIZE 11 DQN_FILE_SCOPE bool Dqn_StrReverse (char *buf, const i32 bufSize); DQN_FILE_SCOPE i32 Dqn_StrFindFirstOccurence(const char *const src, const i32 srcLen, const char *const find, const i32 findLen); DQN_FILE_SCOPE bool Dqn_StrHasSubstring (const char *const src, const i32 srcLen, const char *const find, const i32 findLen); +#define DQN_I32_TO_STR_MAX_BUF_SIZE 11 DQN_FILE_SCOPE i32 Dqn_StrToI32(const char *const buf, const i32 bufSize); // Return the len of the derived string DQN_FILE_SCOPE i32 Dqn_I32ToStr(i32 value, char *buf, i32 bufSize); @@ -3163,12 +3163,14 @@ FILE_SCOPE f64 DqnWin32_QueryPerfCounterTimeInSInternal() f64 DqnTime_NowInS() { + f64 result; #ifdef DQN_WIN32_IMPLEMENTATION - return DqnWin32_QueryPerfCounterTimeInSInternal(); + result = DQN_MAX(DqnWin32_QueryPerfCounterTimeInSInternal(), 0); #else + result = 0; DQN_ASSERT(DQN_INVALID_CODE_PATH); - return 0; #endif + return result; }; f64 DqnTime_NowInMs() { return DqnTime_NowInS() * 1000.0f; }