|
@@ -4,8 +4,8 @@ |
|
|
#include <dsound.h> |
|
|
#include <dsound.h> |
|
|
|
|
|
|
|
|
// Local imports |
|
|
// Local imports |
|
|
|
|
|
#include "handmade.h" |
|
|
#include "win32_handmade.h" |
|
|
#include "win32_handmade.h" |
|
|
#include "handmade.cpp" |
|
|
|
|
|
|
|
|
|
|
|
global ATOM HH_CTRLW; |
|
|
global ATOM HH_CTRLW; |
|
|
global bool globalRunning; |
|
|
global bool globalRunning; |
|
@@ -31,13 +31,23 @@ typedef HRESULT WINAPI DirectSoundCreateFn(LPCGUID pcGuidDevice, LPDIRECTSOUND * |
|
|
|
|
|
|
|
|
#define stackAlloc(size, type) (type*)_alloca(size*sizeof(type)) |
|
|
#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) { |
|
|
if (fileMemory) { |
|
|
VirtualFree(fileMemory, NULL, MEM_RELEASE); |
|
|
VirtualFree(fileMemory, NULL, MEM_RELEASE); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
DebugReadFileResult DEBUG_platformReadEntireFile(char *filename) { |
|
|
|
|
|
|
|
|
DEBUG_PLATFORM_READ_ENTIRE_FILE(debugReadEntireFile) { |
|
|
DebugReadFileResult result = {}; |
|
|
DebugReadFileResult result = {}; |
|
|
HANDLE fileHandle = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL); |
|
|
HANDLE fileHandle = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL); |
|
|
if (fileHandle != INVALID_HANDLE_VALUE) { |
|
|
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)) { |
|
|
if (ReadFile(fileHandle, result.contents, (DWORD)fileSize.QuadPart, &bytesRead, NULL) && (fileSize32 == (uint32)bytesRead)) { |
|
|
result.contentsSize = fileSize32; |
|
|
result.contentsSize = fileSize32; |
|
|
} else { |
|
|
} else { |
|
|
DEBUG_platformFreeFileMemory(result.contents); |
|
|
|
|
|
|
|
|
debugFreeFileMemory(result.contents); |
|
|
result.contents = NULL; |
|
|
result.contents = NULL; |
|
|
// logging |
|
|
// logging |
|
|
} |
|
|
} |
|
@@ -68,7 +78,7 @@ DebugReadFileResult DEBUG_platformReadEntireFile(char *filename) { |
|
|
return result; |
|
|
return result; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
bool32 DEBUG_platformWriteEntireFile(char *filename, uint32 memorySize, void *memory) { |
|
|
|
|
|
|
|
|
DEBUG_PLATFORM_WRITE_ENTIRE_FILE(debugWriteEntireFile) { |
|
|
bool32 result = false; |
|
|
bool32 result = false; |
|
|
HANDLE fileHandle = CreateFileA(filename, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, NULL, NULL); |
|
|
HANDLE fileHandle = CreateFileA(filename, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, NULL, NULL); |
|
|
if (fileHandle != INVALID_HANDLE_VALUE) { |
|
|
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() { |
|
|
internal void win32LoadXInput() { |
|
|
HMODULE xInputLib = LoadLibrary("xinput1_4.dll"); |
|
|
HMODULE xInputLib = LoadLibrary("xinput1_4.dll"); |
|
|
if (!xInputLib) { |
|
|
if (!xInputLib) { |
|
@@ -472,6 +518,10 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin |
|
|
GameMemory gameMemory = {}; |
|
|
GameMemory gameMemory = {}; |
|
|
gameMemory.permanentStorageSize = Megabytes(64); |
|
|
gameMemory.permanentStorageSize = Megabytes(64); |
|
|
gameMemory.transientStorageSize = Gigabytes((uint64)4); |
|
|
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; |
|
|
uint64 totalSize = gameMemory.permanentStorageSize + gameMemory.transientStorageSize; |
|
|
gameMemory.permanentStorage = VirtualAlloc(baseAddress, totalSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); |
|
|
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 lastWorkCounter = win32GetWallClock(); |
|
|
LARGE_INTEGER flipWallClock = win32GetWallClock(); |
|
|
LARGE_INTEGER flipWallClock = win32GetWallClock(); |
|
|
|
|
|
|
|
|
int64 lastCycleCount = __rdtsc(); |
|
|
|
|
|
|
|
|
|
|
|
DWORD audioLatencyBytes = 0; |
|
|
DWORD audioLatencyBytes = 0; |
|
|
real32 audioLatencySeconds = 0; |
|
|
real32 audioLatencySeconds = 0; |
|
|
|
|
|
bool soundIsValid = false; |
|
|
|
|
|
|
|
|
|
|
|
Win32GameCode game = win32LoadGameCode(); |
|
|
|
|
|
uint32 loadCounter = 0; |
|
|
|
|
|
|
|
|
|
|
|
int64 lastCycleCount = __rdtsc(); |
|
|
while (globalRunning) { |
|
|
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 *oldKeyboardController = &oldInput->controllers[0]; |
|
|
GameControllerInput *newKeyboardController = &newInput->controllers[0]; |
|
|
GameControllerInput *newKeyboardController = &newInput->controllers[0]; |
|
|
GameControllerInput zeroController = {}; |
|
|
GameControllerInput zeroController = {}; |
|
@@ -566,11 +627,10 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin |
|
|
videoBuffer.memory = globalBackBuffer.memory; |
|
|
videoBuffer.memory = globalBackBuffer.memory; |
|
|
videoBuffer.width = globalBackBuffer.width; |
|
|
videoBuffer.width = globalBackBuffer.width; |
|
|
videoBuffer.height = globalBackBuffer.height; |
|
|
videoBuffer.height = globalBackBuffer.height; |
|
|
gameUpdateAndRender(&gameMemory, &videoBuffer, newInput); |
|
|
|
|
|
|
|
|
game.updateAndRender(&gameMemory, &videoBuffer, newInput); |
|
|
|
|
|
|
|
|
DWORD playCursor = 0; |
|
|
DWORD playCursor = 0; |
|
|
DWORD writeCursor = 0; |
|
|
DWORD writeCursor = 0; |
|
|
bool soundIsValid = false; |
|
|
|
|
|
|
|
|
|
|
|
if (SUCCEEDED(globalSecondaryBuffer->GetCurrentPosition(&playCursor, &writeCursor))) { |
|
|
if (SUCCEEDED(globalSecondaryBuffer->GetCurrentPosition(&playCursor, &writeCursor))) { |
|
|
if (!soundIsValid) { |
|
|
if (!soundIsValid) { |
|
@@ -579,17 +639,17 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin |
|
|
} |
|
|
} |
|
|
DWORD byteToLock = (soundOutput.runningSampleIndex*soundOutput.bytesPerSample) % soundOutput.secondaryBufferSize; |
|
|
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 expectedSoundBytesPerFrame = (soundOutput.samplesPerSecond * soundOutput.bytesPerSample) / gameUpdateHz; |
|
|
DWORD expectedFrameBoundaryByte = playCursor + expectedSoundBytesPerFrame; |
|
|
DWORD expectedFrameBoundaryByte = playCursor + expectedSoundBytesPerFrame; |
|
|
|
|
|
|
|
|
DWORD safeWriteCursor = writeCursor; |
|
|
DWORD safeWriteCursor = writeCursor; |
|
|
if (safeWriteCursor < playCursor) { |
|
|
if (safeWriteCursor < playCursor) { |
|
|
safeWriteCursor += soundOutput.secondaryBufferSize; |
|
|
safeWriteCursor += soundOutput.secondaryBufferSize; |
|
|
} |
|
|
} |
|
|
Assert(safeWriteCursor >= playCursor); |
|
|
Assert(safeWriteCursor >= playCursor); |
|
|
safeWriteCursor += soundOutput.safetyBytes + 2000; |
|
|
|
|
|
bool audioIsLowLatency = safeWriteCursor >= expectedFrameBoundaryByte; |
|
|
|
|
|
|
|
|
safeWriteCursor += soundOutput.safetyBytes; |
|
|
|
|
|
|
|
|
|
|
|
bool audioIsLowLatency = safeWriteCursor < expectedFrameBoundaryByte; |
|
|
|
|
|
|
|
|
DWORD targetCursor = 0; |
|
|
DWORD targetCursor = 0; |
|
|
if (audioIsLowLatency) { |
|
|
if (audioIsLowLatency) { |
|
@@ -610,7 +670,7 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin |
|
|
soundBuffer.samplesPerSecond = soundOutput.samplesPerSecond; |
|
|
soundBuffer.samplesPerSecond = soundOutput.samplesPerSecond; |
|
|
soundBuffer.sampleCount = bytesToWrite / soundOutput.bytesPerSample; |
|
|
soundBuffer.sampleCount = bytesToWrite / soundOutput.bytesPerSample; |
|
|
soundBuffer.samples = samples; |
|
|
soundBuffer.samples = samples; |
|
|
gameGetSoundSamples(&gameMemory, &soundBuffer); |
|
|
|
|
|
|
|
|
game.getSoundSamples(&gameMemory, &soundBuffer); |
|
|
#if HANDMADE_INTERNAL |
|
|
#if HANDMADE_INTERNAL |
|
|
Assert(debugTimeMarkerIndex < ArrayCount(debugMarkers)); |
|
|
Assert(debugTimeMarkerIndex < ArrayCount(debugMarkers)); |
|
|
|
|
|
|
|
|