diff --git a/misc/bdebug.bat b/misc/bdebug.bat index 0b437a1..26f29d8 100644 --- a/misc/bdebug.bat +++ b/misc/bdebug.bat @@ -1,2 +1,2 @@ call build || exit /b %errorlevel% -devenv .\build\handmade.exe +devenv .\build\handmade_win32.exe diff --git a/misc/brun.bat b/misc/brun.bat index 3432b83..900435c 100644 --- a/misc/brun.bat +++ b/misc/brun.bat @@ -1,2 +1,2 @@ call build || exit /b %errorlevel% -.\build\handmade.exe +.\build\handmade_win32.exe diff --git a/misc/build.bat b/misc/build.bat index 6bd865e..687c4ad 100644 --- a/misc/build.bat +++ b/misc/build.bat @@ -1,9 +1,9 @@ @echo off if NOT EXIST .\build mkdir .\build -pushd .\build -cl ^ + +set commonLinkerFlags= -opt:ref user32.lib Gdi32.lib winmm.lib +set commonCompilerFlags=^ -MT %= Make sure the C runtime library is statically linked =%^ - -Fmwin32_handmade.map %= Create a map file =%^ -Gm- %= Turns off incremently building =%^ -nologo %= No one cares you made the compiler Microsoft =%^ -Oi %= Always use intrinsics =%^ @@ -12,10 +12,12 @@ cl ^ -WX -W4 -wd4201 -wd4100 -wd4189 %= Compiler warnings, -WX warnings as errors, -W4 warning level 4, -wdXXXX disable warning XXXX =%^ -DHANDMADE_INTERNAL=1 -DHANDMADE_SLOW=1 -DHANDMADE_WIN32=1 %= Custom #defines =%^ -FC %= Full path of source code file in diagnostics =%^ - -Zi %= Generate debugger info =%^ - -Fe:handmade.exe ..\src\win32_handmade.cpp %= Output filename, input filename =%^ - user32.lib Gdi32.lib winmm.lib %= Linked libraries =%^ - /link -subsystem:windows,5.1 %= Linker stuff =% + -Zi %= Generate debugger info =% + +pushd .\build + +cl %commonCompilerFlags% -Fe:handmade.dll ..\src\handmade.cpp -Fmhandmade.map /link /DLL /EXPORT:gameGetSoundSamples /EXPORT:gameUpdateAndRender +cl %commonCompilerFlags% -Fe:handmade_win32.exe ..\src\win32_handmade.cpp -Fmwin32_handmade.map /link %commonLinkerFlags% popd exit /b diff --git a/src/handmade.cpp b/src/handmade.cpp index 5dc24d6..0648053 100644 --- a/src/handmade.cpp +++ b/src/handmade.cpp @@ -2,16 +2,6 @@ #define PI32 3.141592653589f -void debug_printf(wchar_t* format, ...) { - size_t bufsize = wcslen(format)*10; - wchar_t *output = (wchar_t*)malloc(bufsize); - va_list list; - va_start(list, format); - vswprintf(output, bufsize, format, list); - OutputDebugStringW(output); - free(output); -} - internal void outputSineSound(GameSoundOutputBuffer *soundBuffer, GameState *state) { int16 toneVolume = 3000; int wavePeriod = soundBuffer->samplesPerSecond/state->toneHz; @@ -34,23 +24,25 @@ internal void renderWeirdGradient(GameOffscreenBuffer *buffer, int xOffset, int for (int x = 0; x < buffer->width; x++) { uint8 blue = (uint8)(x + xOffset); uint8 green = (uint8)(y + yOffset); - *pixel++ = (green << 8) | blue; + *pixel++ = (green << 16) | blue; } row += pitch; } } -internal void gameUpdateAndRender(GameMemory *memory, GameOffscreenBuffer *videoBuf, GameInput *input) { +extern "C" GAME_UPDATE_AND_RENDER(gameUpdateAndRender) { Assert(sizeof(GameState) <= memory->permanentStorageSize); GameState *state = (GameState*)memory->permanentStorage; if (!memory->isInitialised) { - //DebugReadFileResult bmpMem = DEBUG_platformReadEntireFile(__FILE__); - //if (bmpMem.contents) { - // DEBUG_platformWriteEntireFile("c:/source/repos/handmade/src/test.cpp", bmpMem.contentsSize, bmpMem.contents); - // DEBUG_platformFreeFileMemory(bmpMem.contents); - //} + DebugReadFileResult bmpMem = memory->debugReadEntireFile(__FILE__); + if (bmpMem.contents) { + if (false) { + memory->debugWriteEntireFile("c:/source/repos/handmade/src/test.cpp", bmpMem.contentsSize, bmpMem.contents); + } + memory->debugFreeFileMemory(bmpMem.contents); + } state->toneHz = 440; state->tSine = 0; @@ -70,8 +62,6 @@ internal void gameUpdateAndRender(GameMemory *memory, GameOffscreenBuffer *video } else if (controllerInput->stickLeft.endedDown) { state->toneHz = 440 - 128; state->greenOffset += 10; - } else { - //state->toneHz = 440; } if (controllerInput->stickUp.endedDown) { state->blueOffset += 10; @@ -84,6 +74,14 @@ internal void gameUpdateAndRender(GameMemory *memory, GameOffscreenBuffer *video renderWeirdGradient(videoBuf, state->greenOffset, state->blueOffset); } -internal void gameGetSoundSamples(GameMemory *memory, GameSoundOutputBuffer *soundBuf) { +extern "C" GAME_GET_SOUND_SAMPLES(gameGetSoundSamples) { outputSineSound(soundBuf, (GameState*)memory->permanentStorage); } + +#if HANDMADE_WIN32 +#include "windows.h" + +BOOL DllMain() { + return TRUE; +} +#endif diff --git a/src/handmade.h b/src/handmade.h index bca6093..ba3d45a 100644 --- a/src/handmade.h +++ b/src/handmade.h @@ -64,6 +64,28 @@ inline uint32 safeTruncateUInt64(uint64 val) { return (uint32)val; } +// === Platform to game services === +#if HANDMADE_INTERNAL + +struct DebugReadFileResult { + uint32 contentsSize; + void *contents; +}; + +#define DEBUG_PLATFORM_READ_ENTIRE_FILE(name) DebugReadFileResult name(char *filename) +typedef DEBUG_PLATFORM_READ_ENTIRE_FILE(DebugPlatformReadEntireFileFn); + +#define DEBUG_PLATFORM_FREE_FILE_MEMORY(name) void name(void *fileMemory) +typedef DEBUG_PLATFORM_FREE_FILE_MEMORY(DebugPlatformFreeFileMemoryFn); + +#define DEBUG_PLATFORM_WRITE_ENTIRE_FILE(name) bool32 name(char *filename, uint32 memorySize, void *memory) +typedef DEBUG_PLATFORM_WRITE_ENTIRE_FILE(DebugPlatformWriteEntireFileFn); + +#define DEBUG_PLATFORM_PRINTF(name) void name(wchar_t* format, ...) +typedef DEBUG_PLATFORM_PRINTF(DebugPrintfFn); + +#endif + // Game to platform layer services struct GameSoundOutputBuffer { int samplesPerSecond; @@ -111,10 +133,17 @@ struct GameInput { struct GameMemory { bool32 isInitialised; + uint64 permanentStorageSize; void *permanentStorage; // required to be initialised to zero at startup + uint64 transientStorageSize; void *transientStorage; // required to be initialised to zero at startup + + DebugPlatformReadEntireFileFn *debugReadEntireFile; + DebugPlatformFreeFileMemoryFn *debugFreeFileMemory; + DebugPlatformWriteEntireFileFn *debugWriteEntireFile; + DebugPrintfFn *debug_printf; }; struct GameState { @@ -125,22 +154,11 @@ struct GameState { }; // === Game to platform services === -internal void gameUpdateAndRender(GameMemory *memory, GameInput *input, GameOffscreenBuffer *videoBuf); -internal void gameGetSoundSamples(GameMemory *memory, GameSoundOutputBuffer *soundBuf); - -// === Platform to game services === -#if HANDMADE_INTERNAL - -struct DebugReadFileResult { - uint32 contentsSize; - void *contents; -}; - -DebugReadFileResult DEBUG_platformReadEntireFile(char *filename); -void DEBUG_platformFreeFileMemory(void *memory); -bool32 DEBUG_platformWriteEntireFile(char *filename, uint32 memorySize, void *memory); -void debug_printf(wchar_t* format, ...); - -#endif +#define GAME_UPDATE_AND_RENDER(name) void name(GameMemory *memory, GameOffscreenBuffer *videoBuf, GameInput *input) +typedef GAME_UPDATE_AND_RENDER(GameUpdateAndRenderFn); +GAME_UPDATE_AND_RENDER(gameUpdateAndRenderStub) {} +#define GAME_GET_SOUND_SAMPLES(name) void name(GameMemory *memory, GameSoundOutputBuffer *soundBuf) +typedef GAME_GET_SOUND_SAMPLES(GameGetSoundSamplesFn); +GAME_GET_SOUND_SAMPLES(gameGetSoundSamplesStub) {} diff --git a/src/win32_handmade.cpp b/src/win32_handmade.cpp index caeff4e..b6324ee 100644 --- a/src/win32_handmade.cpp +++ b/src/win32_handmade.cpp @@ -4,8 +4,8 @@ #include // Local imports +#include "handmade.h" #include "win32_handmade.h" -#include "handmade.cpp" global ATOM HH_CTRLW; global bool globalRunning; @@ -31,13 +31,23 @@ typedef HRESULT WINAPI DirectSoundCreateFn(LPCGUID pcGuidDevice, LPDIRECTSOUND * #define stackAlloc(size, type) (type*)_alloca(size*sizeof(type)) -void DEBUG_platformFreeFileMemory(void *fileMemory) { +DEBUG_PLATFORM_PRINTF(debug_printf) { + size_t bufsize = wcslen(format)*10; + wchar_t *output = (wchar_t*)malloc(bufsize); + va_list list; + va_start(list, format); + vswprintf(output, bufsize, format, list); + OutputDebugStringW(output); + free(output); +} + +DEBUG_PLATFORM_FREE_FILE_MEMORY(debugFreeFileMemory) { if (fileMemory) { VirtualFree(fileMemory, NULL, MEM_RELEASE); } } -DebugReadFileResult DEBUG_platformReadEntireFile(char *filename) { +DEBUG_PLATFORM_READ_ENTIRE_FILE(debugReadEntireFile) { DebugReadFileResult result = {}; HANDLE fileHandle = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL); if (fileHandle != INVALID_HANDLE_VALUE) { @@ -50,7 +60,7 @@ DebugReadFileResult DEBUG_platformReadEntireFile(char *filename) { if (ReadFile(fileHandle, result.contents, (DWORD)fileSize.QuadPart, &bytesRead, NULL) && (fileSize32 == (uint32)bytesRead)) { result.contentsSize = fileSize32; } else { - DEBUG_platformFreeFileMemory(result.contents); + debugFreeFileMemory(result.contents); result.contents = NULL; // logging } @@ -68,7 +78,7 @@ DebugReadFileResult DEBUG_platformReadEntireFile(char *filename) { return result; } -bool32 DEBUG_platformWriteEntireFile(char *filename, uint32 memorySize, void *memory) { +DEBUG_PLATFORM_WRITE_ENTIRE_FILE(debugWriteEntireFile) { bool32 result = false; HANDLE fileHandle = CreateFileA(filename, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, NULL, NULL); if (fileHandle != INVALID_HANDLE_VALUE) { @@ -128,6 +138,42 @@ internal void win32DrawBufferInWindow(Win32OffscreenBuffer *buffer, HWND window) ); } +struct Win32GameCode { + HMODULE gameCodeLib; + GameUpdateAndRenderFn *updateAndRender; + GameGetSoundSamplesFn *getSoundSamples; + bool isValid; +}; + +internal Win32GameCode win32LoadGameCode() { + Win32GameCode result = {}; + + CopyFile("handmade.dll", "handmade_temp.dll", FALSE); + HMODULE gameCodeLib = LoadLibrary("handmade_temp.dll"); + result.gameCodeLib = gameCodeLib; + + if (gameCodeLib) { + result.updateAndRender = (GameUpdateAndRenderFn *)GetProcAddress(result.gameCodeLib, "gameUpdateAndRender"); + result.getSoundSamples = (GameGetSoundSamplesFn *)GetProcAddress(result.gameCodeLib, "gameGetSoundSamples"); + result.isValid = (result.updateAndRender && result.getSoundSamples); + } + if (!result.isValid) { + result.getSoundSamples = gameGetSoundSamplesStub; + result.updateAndRender = gameUpdateAndRenderStub; + } + + return result; +} + +internal void win32UnloadGameCode(Win32GameCode *gameCode) { + if (gameCode->gameCodeLib) { + FreeLibrary(gameCode->gameCodeLib); + } + gameCode->isValid = false; + gameCode->getSoundSamples = gameGetSoundSamplesStub; + gameCode->updateAndRender = gameUpdateAndRenderStub; +} + internal void win32LoadXInput() { HMODULE xInputLib = LoadLibrary("xinput1_4.dll"); if (!xInputLib) { @@ -472,6 +518,10 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin GameMemory gameMemory = {}; gameMemory.permanentStorageSize = Megabytes(64); gameMemory.transientStorageSize = Gigabytes((uint64)4); + gameMemory.debugFreeFileMemory = debugFreeFileMemory; + gameMemory.debugWriteEntireFile = debugWriteEntireFile; + gameMemory.debugReadEntireFile = debugReadEntireFile; + gameMemory.debug_printf = debug_printf; uint64 totalSize = gameMemory.permanentStorageSize + gameMemory.transientStorageSize; gameMemory.permanentStorage = VirtualAlloc(baseAddress, totalSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); @@ -484,12 +534,23 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin LARGE_INTEGER lastWorkCounter = win32GetWallClock(); LARGE_INTEGER flipWallClock = win32GetWallClock(); - int64 lastCycleCount = __rdtsc(); DWORD audioLatencyBytes = 0; real32 audioLatencySeconds = 0; + bool soundIsValid = false; + + Win32GameCode game = win32LoadGameCode(); + uint32 loadCounter = 0; + int64 lastCycleCount = __rdtsc(); while (globalRunning) { + if (loadCounter++ > 60) { + win32UnloadGameCode(&game); + game = win32LoadGameCode(); + loadCounter = 0; + // TODO(dledda): handmade hero episode 22 (from the beginning) + } + GameControllerInput *oldKeyboardController = &oldInput->controllers[0]; GameControllerInput *newKeyboardController = &newInput->controllers[0]; GameControllerInput zeroController = {}; @@ -566,11 +627,10 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin videoBuffer.memory = globalBackBuffer.memory; videoBuffer.width = globalBackBuffer.width; videoBuffer.height = globalBackBuffer.height; - gameUpdateAndRender(&gameMemory, &videoBuffer, newInput); + game.updateAndRender(&gameMemory, &videoBuffer, newInput); DWORD playCursor = 0; DWORD writeCursor = 0; - bool soundIsValid = false; if (SUCCEEDED(globalSecondaryBuffer->GetCurrentPosition(&playCursor, &writeCursor))) { if (!soundIsValid) { @@ -579,17 +639,17 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin } DWORD byteToLock = (soundOutput.runningSampleIndex*soundOutput.bytesPerSample) % soundOutput.secondaryBufferSize; - // TODO(dledda): episode 20 ~2:00, something's wrong though, gotta sort it out. - DWORD expectedSoundBytesPerFrame = (soundOutput.samplesPerSecond * soundOutput.bytesPerSample) / gameUpdateHz; DWORD expectedFrameBoundaryByte = playCursor + expectedSoundBytesPerFrame; + DWORD safeWriteCursor = writeCursor; if (safeWriteCursor < playCursor) { safeWriteCursor += soundOutput.secondaryBufferSize; } Assert(safeWriteCursor >= playCursor); - safeWriteCursor += soundOutput.safetyBytes + 2000; - bool audioIsLowLatency = safeWriteCursor >= expectedFrameBoundaryByte; + safeWriteCursor += soundOutput.safetyBytes; + + bool audioIsLowLatency = safeWriteCursor < expectedFrameBoundaryByte; DWORD targetCursor = 0; if (audioIsLowLatency) { @@ -610,7 +670,7 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin soundBuffer.samplesPerSecond = soundOutput.samplesPerSecond; soundBuffer.sampleCount = bytesToWrite / soundOutput.bytesPerSample; soundBuffer.samples = samples; - gameGetSoundSamples(&gameMemory, &soundBuffer); + game.getSoundSamples(&gameMemory, &soundBuffer); #if HANDMADE_INTERNAL Assert(debugTimeMarkerIndex < ArrayCount(debugMarkers));