|
|
@@ -60,7 +60,7 @@ DEBUG_PLATFORM_READ_ENTIRE_FILE(debugReadEntireFile) { |
|
|
|
if (ReadFile(fileHandle, result.contents, (DWORD)fileSize.QuadPart, &bytesRead, NULL) && (fileSize32 == (uint32)bytesRead)) { |
|
|
|
result.contentsSize = fileSize32; |
|
|
|
} else { |
|
|
|
debugFreeFileMemory(result.contents); |
|
|
|
debugFreeFileMemory(ctx, result.contents); |
|
|
|
result.contents = NULL; |
|
|
|
// logging |
|
|
|
} |
|
|
@@ -139,14 +139,6 @@ internal void win32DrawBufferInWindow(Win32OffscreenBuffer *buffer, HWND window) |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
struct Win32GameCode { |
|
|
|
HMODULE gameCodeLib; |
|
|
|
GameUpdateAndRenderFn *updateAndRender; |
|
|
|
GameGetSoundSamplesFn *getSoundSamples; |
|
|
|
bool isValid; |
|
|
|
FILETIME lastWriteTime; |
|
|
|
}; |
|
|
|
|
|
|
|
inline FILETIME win32GetLastWriteTime(char *filename) { |
|
|
|
FILETIME lastWriteTime = {}; |
|
|
|
WIN32_FIND_DATA findData; |
|
|
@@ -202,8 +194,10 @@ internal void win32LoadXInput() { |
|
|
|
} |
|
|
|
|
|
|
|
internal void win32ProcessKeyboardKeypress(GameButtonState *newState, bool32 isDown) { |
|
|
|
newState->endedDown = isDown; |
|
|
|
newState->halfTransitionCount++; |
|
|
|
if (newState->endedDown != isDown) { |
|
|
|
newState->endedDown = isDown; |
|
|
|
newState->halfTransitionCount++; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
internal void win32ProcessXInputDigitalButton(DWORD xInputButtonState, GameButtonState *oldState, GameButtonState *newState, DWORD buttonBit) { |
|
|
@@ -483,12 +477,6 @@ inline real32 win32GetSecondsElapsed(LARGE_INTEGER start, LARGE_INTEGER end) { |
|
|
|
return (real32)(end.QuadPart - start.QuadPart) / (real32)globalPerfCountFrequency; |
|
|
|
} |
|
|
|
|
|
|
|
// int monitorRefreshHz = 60; |
|
|
|
// int gameUpdateHz = monitorRefreshHz / 2; |
|
|
|
|
|
|
|
#define monitorRefreshHz 60 |
|
|
|
#define gameUpdateHz (monitorRefreshHz / 2) |
|
|
|
|
|
|
|
internal void win32DebugDrawVertical(Win32OffscreenBuffer *buffer, int x, int top, int bottom, int32 color) { |
|
|
|
uint8 *pixel = (uint8 *)buffer->memory + top * buffer->pitch + x*buffer->bytesPerPixel; |
|
|
|
for (int y = top; y < bottom; y++) { |
|
|
@@ -503,29 +491,6 @@ inline void win32DrawSoundBufferMarker(Win32OffscreenBuffer *buffer, Win32SoundO |
|
|
|
win32DebugDrawVertical(buffer, x, top, bottom, color); |
|
|
|
} |
|
|
|
|
|
|
|
internal void win32DebugSyncDisplay(Win32OffscreenBuffer *buffer, int markerCount, Win32DebugTimeMarker *markers, Win32SoundOutput *soundOutput, real32 targetSecondsPerFrame) { |
|
|
|
int padX = 16; |
|
|
|
int padY = 16; |
|
|
|
|
|
|
|
int top = padY; |
|
|
|
int bottom = buffer->height - padY; |
|
|
|
int renderWidth = buffer->width - 2 *padX; |
|
|
|
|
|
|
|
real32 pxPerSoundBufferEntry = (real32)renderWidth / (real32)soundOutput->secondaryBufferSize; |
|
|
|
|
|
|
|
#if 0 |
|
|
|
for (int markerIndex = 0; markerIndex < markerCount; markerIndex++) { |
|
|
|
Win32DebugTimeMarker *thisMarker = &markers[markerIndex]; |
|
|
|
real32 alpha = ((real32)(markerIndex + 1) / (real32)markerCount); |
|
|
|
int x = padX + (int)(pxPerSoundBufferEntry * (real32)thisMarker->writeCursor); |
|
|
|
uint8 *pixel = (uint8 *)buffer->memory + top * buffer->pitch + x*buffer->bytesPerPixel; |
|
|
|
uint32 newPixel = (uint32)((real32)alpha * 0xFFFFFFFF + (1.0f - alpha) * (*(uint32 *)pixel)); |
|
|
|
win32DrawSoundBufferMarker(buffer, soundOutput, pxPerSoundBufferEntry, padX, top, bottom, thisMarker->playCursor, newPixel); |
|
|
|
win32DrawSoundBufferMarker(buffer, soundOutput, pxPerSoundBufferEntry, padX, top, bottom, thisMarker->writeCursor, 0x00FF0000); |
|
|
|
} |
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
|
void catStrings(size_t sourceACount, char *sourceA, size_t sourceBCount, char *sourceB, size_t destCount, char *dest) { |
|
|
|
for (int i = 0; i < sourceACount; i++) { |
|
|
|
*dest++ = *sourceA++; |
|
|
@@ -536,6 +501,9 @@ void catStrings(size_t sourceACount, char *sourceA, size_t sourceBCount, char *s |
|
|
|
*dest++ = 0; |
|
|
|
} |
|
|
|
|
|
|
|
#define WINDOW_WIDTH 1280 |
|
|
|
#define WINDOW_HEIGHT 720 |
|
|
|
|
|
|
|
int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLine, int commandShow) { |
|
|
|
char exeFileName[MAX_PATH]; |
|
|
|
DWORD sizeOfFilename = GetModuleFileNameA(NULL, exeFileName, sizeof(exeFileName)); |
|
|
@@ -570,27 +538,36 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin |
|
|
|
windowClass.lpfnWndProc = &mainWindowCallback; |
|
|
|
windowClass.hInstance = instance; |
|
|
|
windowClass.lpszClassName = "HandmadeHeroWindowClass"; |
|
|
|
|
|
|
|
resizeDIBSection(&globalBackBuffer, 1280, 720); |
|
|
|
|
|
|
|
real32 targetSecondsPerFrame = 1.0f / (real32)gameUpdateHz; |
|
|
|
resizeDIBSection(&globalBackBuffer, WINDOW_WIDTH, WINDOW_HEIGHT); |
|
|
|
|
|
|
|
if (RegisterClass(&windowClass)) { |
|
|
|
DWORD windowFlags = WS_OVERLAPPEDWINDOW | WS_VISIBLE; |
|
|
|
RECT rect = { 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT }; |
|
|
|
AdjustWindowRectEx(&rect, windowFlags, false, NULL); |
|
|
|
HWND window = CreateWindowExA( |
|
|
|
NULL, |
|
|
|
windowClass.lpszClassName, |
|
|
|
"Handmade Hero", |
|
|
|
WS_OVERLAPPEDWINDOW | WS_VISIBLE, |
|
|
|
CW_USEDEFAULT, |
|
|
|
CW_USEDEFAULT, |
|
|
|
windowFlags, |
|
|
|
CW_USEDEFAULT, |
|
|
|
CW_USEDEFAULT, |
|
|
|
rect.right - rect.left, |
|
|
|
rect.bottom - rect.top, |
|
|
|
NULL, |
|
|
|
NULL, |
|
|
|
instance, |
|
|
|
NULL |
|
|
|
); |
|
|
|
|
|
|
|
if (window) { |
|
|
|
int monitorRefreshHz = 60; |
|
|
|
int win32RefreshRate = GetDeviceCaps(GetDC(window), VREFRESH); |
|
|
|
if (win32RefreshRate > 1) { |
|
|
|
monitorRefreshHz = win32RefreshRate; |
|
|
|
} |
|
|
|
real32 gameUpdateHz = monitorRefreshHz / 2.0f; |
|
|
|
real32 targetSecondsPerFrame = 1.0f / (real32)gameUpdateHz; |
|
|
|
|
|
|
|
HH_CTRLW = GlobalAddAtomA("HH_CTRLW"); |
|
|
|
RegisterHotKey(window, HH_CTRLW, MOD_CONTROL, 'W'); |
|
|
|
|
|
|
@@ -601,9 +578,6 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin |
|
|
|
GameInput *oldInput = &input[0]; |
|
|
|
GameInput *newInput = &input[1]; |
|
|
|
|
|
|
|
int debugTimeMarkerIndex = 0; |
|
|
|
Win32DebugTimeMarker debugMarkers[gameUpdateHz / 2] = {}; |
|
|
|
|
|
|
|
win32LoadXInput(); |
|
|
|
|
|
|
|
// sound test |
|
|
@@ -612,7 +586,7 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin |
|
|
|
soundOutput.runningSampleIndex = 0; |
|
|
|
soundOutput.bytesPerSample = sizeof(int16)*2; |
|
|
|
soundOutput.secondaryBufferSize = soundOutput.samplesPerSecond*soundOutput.bytesPerSample; |
|
|
|
soundOutput.safetyBytes = (soundOutput.samplesPerSecond * soundOutput.bytesPerSample / gameUpdateHz) / 2; |
|
|
|
soundOutput.safetyBytes = (int)((soundOutput.samplesPerSecond * soundOutput.bytesPerSample / gameUpdateHz) / 2.0f); |
|
|
|
|
|
|
|
int16 *samples = (int16*)VirtualAlloc(NULL, soundOutput.secondaryBufferSize, MEM_COMMIT, PAGE_READWRITE); |
|
|
|
|
|
|
@@ -641,7 +615,6 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin |
|
|
|
LARGE_INTEGER lastWorkCounter = win32GetWallClock(); |
|
|
|
LARGE_INTEGER flipWallClock = win32GetWallClock(); |
|
|
|
|
|
|
|
|
|
|
|
DWORD audioLatencyBytes = 0; |
|
|
|
real32 audioLatencySeconds = 0; |
|
|
|
bool soundIsValid = false; |
|
|
@@ -666,6 +639,20 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin |
|
|
|
|
|
|
|
win32ProcessPendingMessages(&win32State, newKeyboardController); |
|
|
|
|
|
|
|
POINT mousePos; |
|
|
|
GetCursorPos(&mousePos); |
|
|
|
ScreenToClient(window, &mousePos); |
|
|
|
newInput->mouseX = mousePos.x; |
|
|
|
newInput->mouseY = mousePos.y; |
|
|
|
newInput->mouseZ = 0; // mouse wheel |
|
|
|
win32ProcessKeyboardKeypress(&newInput->mouseButtons[0], GetKeyState(VK_LBUTTON) & (1 << 15)); |
|
|
|
win32ProcessKeyboardKeypress(&newInput->mouseButtons[1], GetKeyState(VK_MBUTTON) & (1 << 15)); |
|
|
|
win32ProcessKeyboardKeypress(&newInput->mouseButtons[2], GetKeyState(VK_RBUTTON) & (1 << 15)); |
|
|
|
win32ProcessKeyboardKeypress(&newInput->mouseButtons[3], GetKeyState(VK_XBUTTON1) & (1 << 15)); |
|
|
|
win32ProcessKeyboardKeypress(&newInput->mouseButtons[4], GetKeyState(VK_XBUTTON2) & (1 << 15)); |
|
|
|
|
|
|
|
// TODO(dledda): day 25 34:00 |
|
|
|
|
|
|
|
XINPUT_STATE controllerState; |
|
|
|
int maxControllerCount = XUSER_MAX_COUNT; |
|
|
|
if (maxControllerCount > ArrayCount(newInput->controllers) - 1) { |
|
|
@@ -728,6 +715,8 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
ThreadContext threadCtx = {}; |
|
|
|
|
|
|
|
GameOffscreenBuffer videoBuffer = {}; |
|
|
|
videoBuffer.memory = globalBackBuffer.memory; |
|
|
|
videoBuffer.width = globalBackBuffer.width; |
|
|
@@ -742,20 +731,25 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin |
|
|
|
win32PlaybackInput(&win32State, newInput); |
|
|
|
} |
|
|
|
|
|
|
|
game.updateAndRender(&gameMemory, &videoBuffer, newInput); |
|
|
|
if (game.updateAndRender) { |
|
|
|
game.updateAndRender(&threadCtx, &gameMemory, &videoBuffer, newInput); |
|
|
|
} |
|
|
|
|
|
|
|
LARGE_INTEGER audioWallClock = win32GetWallClock(); |
|
|
|
real32 fromBeginToAudioSeconds = win32GetSecondsElapsed(flipWallClock, audioWallClock); |
|
|
|
|
|
|
|
DWORD playCursor = 0; |
|
|
|
DWORD writeCursor = 0; |
|
|
|
|
|
|
|
if (SUCCEEDED(globalSecondaryBuffer->GetCurrentPosition(&playCursor, &writeCursor))) { |
|
|
|
if (!soundIsValid) { |
|
|
|
soundOutput.runningSampleIndex = writeCursor / soundOutput.bytesPerSample; |
|
|
|
soundIsValid = true; |
|
|
|
} |
|
|
|
DWORD byteToLock = (soundOutput.runningSampleIndex*soundOutput.bytesPerSample) % soundOutput.secondaryBufferSize; |
|
|
|
|
|
|
|
DWORD expectedSoundBytesPerFrame = (soundOutput.samplesPerSecond * soundOutput.bytesPerSample) / gameUpdateHz; |
|
|
|
DWORD expectedFrameBoundaryByte = playCursor + expectedSoundBytesPerFrame; |
|
|
|
DWORD expectedSoundBytesPerFrame = (DWORD)((soundOutput.samplesPerSecond * soundOutput.bytesPerSample) / gameUpdateHz); |
|
|
|
real32 secondsLeftUntilFlip = targetSecondsPerFrame - fromBeginToAudioSeconds; |
|
|
|
DWORD expectedBytesUntilFlip = (DWORD)((secondsLeftUntilFlip/targetSecondsPerFrame) * (real32)expectedSoundBytesPerFrame); |
|
|
|
DWORD expectedFrameBoundaryByte = playCursor + expectedBytesUntilFlip; |
|
|
|
|
|
|
|
DWORD safeWriteCursor = writeCursor; |
|
|
|
if (safeWriteCursor < playCursor) { |
|
|
@@ -785,25 +779,7 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin |
|
|
|
soundBuffer.samplesPerSecond = soundOutput.samplesPerSecond; |
|
|
|
soundBuffer.sampleCount = bytesToWrite / soundOutput.bytesPerSample; |
|
|
|
soundBuffer.samples = samples; |
|
|
|
game.getSoundSamples(&gameMemory, &soundBuffer); |
|
|
|
#if HANDMADE_INTERNAL |
|
|
|
Assert(debugTimeMarkerIndex < ArrayCount(debugMarkers)); |
|
|
|
|
|
|
|
Win32DebugTimeMarker *marker = &debugMarkers[debugTimeMarkerIndex++]; |
|
|
|
|
|
|
|
DWORD unwrappedWriteCursor = writeCursor; |
|
|
|
if (unwrappedWriteCursor < targetCursor) { |
|
|
|
unwrappedWriteCursor += soundOutput.secondaryBufferSize; |
|
|
|
} |
|
|
|
audioLatencyBytes = unwrappedWriteCursor - playCursor; |
|
|
|
audioLatencySeconds = (((real32)audioLatencyBytes / (real32)soundOutput.bytesPerSample) / (real32)soundOutput.samplesPerSecond); |
|
|
|
|
|
|
|
if (debugTimeMarkerIndex >= ArrayCount(debugMarkers)) { |
|
|
|
debugTimeMarkerIndex = 0; |
|
|
|
} |
|
|
|
|
|
|
|
globalSecondaryBuffer->GetCurrentPosition(&marker->playCursor, &marker->writeCursor); |
|
|
|
#endif |
|
|
|
game.getSoundSamples(&threadCtx, &gameMemory, &soundBuffer); |
|
|
|
win32FillSoundBuffer(&soundOutput, byteToLock, bytesToWrite, &soundBuffer); |
|
|
|
} else { |
|
|
|
soundIsValid = false; |
|
|
@@ -827,10 +803,6 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin |
|
|
|
|
|
|
|
lastWorkCounter = win32GetWallClock(); |
|
|
|
|
|
|
|
#if HANDMADE_INTERNAL |
|
|
|
win32DebugSyncDisplay(&globalBackBuffer, ArrayCount(debugMarkers), debugMarkers, &soundOutput, targetSecondsPerFrame); |
|
|
|
#endif |
|
|
|
|
|
|
|
win32DrawBufferInWindow(&globalBackBuffer, window); |
|
|
|
flipWallClock = win32GetWallClock(); |
|
|
|
|
|
|
|