This commit is contained in:
2024-09-06 22:11:26 +01:00
parent 0941000e13
commit 51c9509a13
5 changed files with 137 additions and 23 deletions

5
.clangd Normal file
View File

@@ -0,0 +1,5 @@
CompileFlags:
Add:
- -DHANDMADE_INTERNAL
- -DHANDMADE_SLOW
- -DHANDMADE_WIN32

View File

@@ -14,7 +14,7 @@ cl ^
-FC %= Full path of source code file in diagnostics =%^ -FC %= Full path of source code file in diagnostics =%^
-Zi %= Generate debugger info =%^ -Zi %= Generate debugger info =%^
-Fe:handmade.exe ..\src\win32_handmade.cpp %= Output filename, input filename =%^ -Fe:handmade.exe ..\src\win32_handmade.cpp %= Output filename, input filename =%^
user32.lib Gdi32.lib %= Linked libraries =%^ user32.lib Gdi32.lib winmm.lib %= Linked libraries =%^
/link -subsystem:windows,5.1 %= Linker stuff =% /link -subsystem:windows,5.1 %= Linker stuff =%
popd popd
exit /b exit /b

View File

@@ -26,7 +26,7 @@
*/ */
#if HANDMADE_SLOW #if HANDMADE_SLOW
#define Assert(Expression) if (!(Expression)) {*(int *)0 = 0;} #define Assert(Expression) if (!(Expression)) {*(volatile int *)0 = 0;}
#else #else
#define Assert(Expression) #define Assert(Expression)
#endif #endif

View File

@@ -3,6 +3,7 @@
#include <Xinput.h> #include <Xinput.h>
#include <dsound.h> #include <dsound.h>
// Local imports
#include "win32_handmade.h" #include "win32_handmade.h"
#include "handmade.cpp" #include "handmade.cpp"
@@ -10,6 +11,7 @@ global ATOM HH_CTRLW;
global bool globalRunning; global bool globalRunning;
global Win32OffscreenBuffer globalBackBuffer; global Win32OffscreenBuffer globalBackBuffer;
global LPDIRECTSOUNDBUFFER globalSecondaryBuffer; global LPDIRECTSOUNDBUFFER globalSecondaryBuffer;
global int64 globalPerfCountFrequency;
// XInputGetState // XInputGetState
#define X_INPUT_GET_STATE(name) DWORD WINAPI name(DWORD dwUserIndex, XINPUT_STATE *pState) #define X_INPUT_GET_STATE(name) DWORD WINAPI name(DWORD dwUserIndex, XINPUT_STATE *pState)
@@ -91,6 +93,7 @@ internal void resizeDIBSection(Win32OffscreenBuffer *buffer, int width, int heig
buffer->width = width; buffer->width = width;
buffer->height = height; buffer->height = height;
buffer->bytesPerPixel = 4;
buffer->info.bmiHeader.biSize = sizeof(buffer->info.bmiHeader); buffer->info.bmiHeader.biSize = sizeof(buffer->info.bmiHeader);
buffer->info.bmiHeader.biWidth = buffer->width; buffer->info.bmiHeader.biWidth = buffer->width;
@@ -351,10 +354,62 @@ internal void win32ProcessPendingMessages(GameControllerInput *keyboardControlle
} }
} }
inline LARGE_INTEGER win32GetWallClock() {
LARGE_INTEGER result;
QueryPerformanceCounter(&result);
return result;
}
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 framesOfAudioLatency 3
#define monitorRefreshHz 60
#define gameUpdateHz (monitorRefreshHz / 2)
internal void win32DebugDrawVertical(Win32OffscreenBuffer *screenBuffer, int x, int top, int bottom, int32 color) {
int pitch = screenBuffer->width*screenBuffer->bytesPerPixel;
uint8 *pixel = (uint8 *)screenBuffer->memory + top * pitch + x*screenBuffer->bytesPerPixel;
for (int y = top; y < bottom; y++) {
*(uint32 *)pixel = color;
pixel += pitch;
}
}
inline void win32DrawSoundBufferMarker(Win32OffscreenBuffer *buffer, Win32SoundOutput *soundOutput, real32 pxPerSoundBufferEntry, int padX, int top, int bottom, DWORD value, uint32 color) {
Assert(value < (DWORD)soundOutput->secondaryBufferSize);
int x = padX + (int)(pxPerSoundBufferEntry * (real32)value);
win32DebugDrawVertical(buffer, x, top, bottom, color);
}
internal void win32DebugSyncDisplay(Win32OffscreenBuffer *screenBuffer, int markerCount, Win32DebugTimeMarker *markers, Win32SoundOutput *soundOutput, real32 targetSecondsPerFrame) {
int padX = 16;
int padY = 16;
int top = padY;
int bottom = screenBuffer->height - padY;
int renderWidth = screenBuffer->width - 2 *padX;
real32 pxPerSoundBufferEntry = (real32)renderWidth / (real32)soundOutput->secondaryBufferSize;
for (int markerIndex = 0; markerIndex < markerCount; markerIndex++) {
Win32DebugTimeMarker *thisMarker = &markers[markerIndex];
win32DrawSoundBufferMarker(screenBuffer, soundOutput, pxPerSoundBufferEntry, padX, top, bottom, thisMarker->playCursor, 0xFFFFFFFF);
win32DrawSoundBufferMarker(screenBuffer, soundOutput, pxPerSoundBufferEntry, padX, top, bottom, thisMarker->writeCursor, 0xFFFF0000);
}
}
int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLine, int commandShow) { int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLine, int commandShow) {
LARGE_INTEGER performanceFrequencyResult; LARGE_INTEGER performanceFrequencyResult;
QueryPerformanceFrequency(&performanceFrequencyResult); QueryPerformanceFrequency(&performanceFrequencyResult);
int64 performanceFrequency = performanceFrequencyResult.QuadPart; globalPerfCountFrequency = performanceFrequencyResult.QuadPart;
UINT desiredSchedulerMs = 1;
bool32 sleepIsGranular = TIMERR_NOERROR == timeBeginPeriod(desiredSchedulerMs);
WNDCLASSA windowClass = {}; WNDCLASSA windowClass = {};
windowClass.style = CS_VREDRAW | CS_HREDRAW; windowClass.style = CS_VREDRAW | CS_HREDRAW;
@@ -364,6 +419,8 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin
resizeDIBSection(&globalBackBuffer, 1280, 720); resizeDIBSection(&globalBackBuffer, 1280, 720);
real32 targetSecondsPerFrame = 1.0f / (real32)gameUpdateHz;
if (RegisterClass(&windowClass)) { if (RegisterClass(&windowClass)) {
HWND window = CreateWindowExA( HWND window = CreateWindowExA(
NULL, NULL,
@@ -388,6 +445,9 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin
GameInput *oldInput = &input[0]; GameInput *oldInput = &input[0];
GameInput *newInput = &input[1]; GameInput *newInput = &input[1];
int debugTimeMarkerIndex = 0;
Win32DebugTimeMarker debugMarkers[gameUpdateHz / 2] = {};
win32LoadXInput(); win32LoadXInput();
// sound test // sound test
@@ -396,7 +456,7 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin
soundOutput.runningSampleIndex = 0; soundOutput.runningSampleIndex = 0;
soundOutput.bytesPerSample = sizeof(int16)*2; soundOutput.bytesPerSample = sizeof(int16)*2;
soundOutput.secondaryBufferSize = soundOutput.samplesPerSecond*soundOutput.bytesPerSample; soundOutput.secondaryBufferSize = soundOutput.samplesPerSecond*soundOutput.bytesPerSample;
soundOutput.latencySampleCount = (int)(soundOutput.samplesPerSecond / 15.0f); soundOutput.latencySampleCount = framesOfAudioLatency * (soundOutput.samplesPerSecond / gameUpdateHz);
int16 *samples = (int16*)VirtualAlloc(NULL, soundOutput.secondaryBufferSize, MEM_COMMIT, PAGE_READWRITE); int16 *samples = (int16*)VirtualAlloc(NULL, soundOutput.secondaryBufferSize, MEM_COMMIT, PAGE_READWRITE);
@@ -417,9 +477,13 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin
win32ClearBuffer(&soundOutput); win32ClearBuffer(&soundOutput);
globalSecondaryBuffer->Play(0, 0, DSBPLAY_LOOPING); globalSecondaryBuffer->Play(0, 0, DSBPLAY_LOOPING);
LARGE_INTEGER lastCounter; LARGE_INTEGER lastWorkCounter = win32GetWallClock();
QueryPerformanceCounter(&lastCounter);
int64 lastCycleCount = __rdtsc(); int64 lastCycleCount = __rdtsc();
bool32 soundIsValid = false;
DWORD lastPlayCursor = 0;
while (globalRunning) { while (globalRunning) {
GameControllerInput *oldKeyboardController = &oldInput->controllers[0]; GameControllerInput *oldKeyboardController = &oldInput->controllers[0];
GameControllerInput *newKeyboardController = &newInput->controllers[0]; GameControllerInput *newKeyboardController = &newInput->controllers[0];
@@ -499,15 +563,12 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin
videoBuffer.height = globalBackBuffer.height; videoBuffer.height = globalBackBuffer.height;
// Sound test // Sound test
DWORD playCursor = 0;
DWORD writeCursor = 0;
DWORD byteToLock = 0; DWORD byteToLock = 0;
DWORD targetCursor = 0; DWORD targetCursor = lastPlayCursor;
DWORD bytesToWrite = 0; DWORD bytesToWrite = 0;
bool soundIsValid = true; if (soundIsValid) {
if (SUCCEEDED(globalSecondaryBuffer->GetCurrentPosition(&playCursor, &writeCursor))) {
byteToLock = (soundOutput.runningSampleIndex*soundOutput.bytesPerSample) % soundOutput.secondaryBufferSize; byteToLock = (soundOutput.runningSampleIndex*soundOutput.bytesPerSample) % soundOutput.secondaryBufferSize;
targetCursor = (playCursor + soundOutput.latencySampleCount*soundOutput.bytesPerSample) % soundOutput.secondaryBufferSize; targetCursor = (lastPlayCursor + soundOutput.latencySampleCount*soundOutput.bytesPerSample) % soundOutput.secondaryBufferSize;
if (byteToLock == targetCursor) { if (byteToLock == targetCursor) {
bytesToWrite = 0; bytesToWrite = 0;
} else if (byteToLock > targetCursor) { } else if (byteToLock > targetCursor) {
@@ -515,7 +576,6 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin
} else { } else {
bytesToWrite = targetCursor - byteToLock; bytesToWrite = targetCursor - byteToLock;
} }
soundIsValid = true;
} }
GameSoundOutputBuffer soundBuffer = {}; GameSoundOutputBuffer soundBuffer = {};
@@ -524,23 +584,66 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin
soundBuffer.samples = samples; soundBuffer.samples = samples;
gameUpdateAndRender(&gameMemory, &videoBuffer, newInput, &soundBuffer); gameUpdateAndRender(&gameMemory, &videoBuffer, newInput, &soundBuffer);
if (soundIsValid) { if (soundIsValid) {
win32FillSoundBuffer(&soundOutput, byteToLock, bytesToWrite, &soundBuffer); win32FillSoundBuffer(&soundOutput, byteToLock, bytesToWrite, &soundBuffer);
} }
real32 secondsElapsedForFrame = win32GetSecondsElapsed(lastWorkCounter, win32GetWallClock());
if (secondsElapsedForFrame < targetSecondsPerFrame) {
if (sleepIsGranular) {
DWORD sleepMs = (DWORD)(1000.0f * (targetSecondsPerFrame - secondsElapsedForFrame));
if (sleepMs > 0) {
Sleep(sleepMs);
}
}
while (secondsElapsedForFrame < targetSecondsPerFrame) {
secondsElapsedForFrame = win32GetSecondsElapsed(lastWorkCounter, win32GetWallClock());
}
} else {
// logging, MISSED FRAME!
}
lastWorkCounter = win32GetWallClock();
#if HANDMADE_INTERNAL
win32DebugSyncDisplay(&globalBackBuffer, ArrayCount(debugMarkers), debugMarkers, &soundOutput, targetSecondsPerFrame);
#endif
win32DrawBufferInWindow(&globalBackBuffer, window); win32DrawBufferInWindow(&globalBackBuffer, window);
LARGE_INTEGER endCounter; DWORD playCursor = 0;
QueryPerformanceCounter(&endCounter); DWORD writeCursor = 0;
if (SUCCEEDED(globalSecondaryBuffer->GetCurrentPosition(&playCursor, &writeCursor))) {
lastPlayCursor = playCursor;
if (!soundIsValid) {
soundOutput.runningSampleIndex = writeCursor / soundOutput.bytesPerSample;
soundIsValid = true;
}
} else {
soundIsValid = false;
}
#if HANDMADE_INTERNAL
{
Assert(debugTimeMarkerIndex < ArrayCount(debugMarkers));
Win32DebugTimeMarker *marker = &debugMarkers[debugTimeMarkerIndex++];
if (debugTimeMarkerIndex >= ArrayCount(debugMarkers)) {
debugTimeMarkerIndex = 0;
}
globalSecondaryBuffer->GetCurrentPosition(&marker->playCursor, &marker->writeCursor);
}
#endif
#if 0
real64 msElapsed = (real32)(1000.0f*secondsElapsedForFrame) / globalPerfCountFrequency;
real64 fps = (real32)(1000.0f*globalPerfCountFrequency/(real32)secondsElapsedForFrame)/1000.0f;
int64 endCycleCount = __rdtsc(); int64 endCycleCount = __rdtsc();
int64 cyclesElapsed = endCycleCount - lastCycleCount; int64 cyclesElapsed = endCycleCount - lastCycleCount;
lastCycleCount = endCycleCount; lastCycleCount = endCycleCount;
#endif
int64 counterElapsed = endCounter.QuadPart - lastCounter.QuadPart;
real32 msElapsed = (real32)(1000.0f*counterElapsed) / performanceFrequency;
real32 fps = (real32)(1000.0f*performanceFrequency/(real32)counterElapsed)/1000.0f;
lastCounter = endCounter;
GameInput *temp = newInput; GameInput *temp = newInput;
newInput = oldInput; newInput = oldInput;
@@ -552,6 +655,7 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin
} else { } else {
// failed // failed
} }
return(0);
return 0;
} }

View File

@@ -22,3 +22,8 @@ struct Win32SoundOutput {
real32 tSine; real32 tSine;
int latencySampleCount; int latencySampleCount;
}; };
struct Win32DebugTimeMarker {
DWORD playCursor;
DWORD writeCursor;
};