|
|
@@ -367,7 +367,6 @@ inline real32 win32GetSecondsElapsed(LARGE_INTEGER start, LARGE_INTEGER end) { |
|
|
|
// int monitorRefreshHz = 60; |
|
|
|
// int gameUpdateHz = monitorRefreshHz / 2; |
|
|
|
|
|
|
|
#define framesOfAudioLatency 3 |
|
|
|
#define monitorRefreshHz 60 |
|
|
|
#define gameUpdateHz (monitorRefreshHz / 2) |
|
|
|
|
|
|
@@ -395,11 +394,16 @@ internal void win32DebugSyncDisplay(Win32OffscreenBuffer *screenBuffer, int mark |
|
|
|
int renderWidth = screenBuffer->width - 2 *padX; |
|
|
|
|
|
|
|
real32 pxPerSoundBufferEntry = (real32)renderWidth / (real32)soundOutput->secondaryBufferSize; |
|
|
|
int pitch = screenBuffer->width*screenBuffer->bytesPerPixel; |
|
|
|
|
|
|
|
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); |
|
|
|
real32 alpha = ((real32)(markerIndex + 1) / (real32)markerCount); |
|
|
|
int x = padX + (int)(pxPerSoundBufferEntry * (real32)thisMarker->writeCursor); |
|
|
|
uint8 *pixel = (uint8 *)screenBuffer->memory + top * pitch + x*screenBuffer->bytesPerPixel; |
|
|
|
uint32 newPixel = (uint32)((real32)alpha * 0xFFFFFFFF + (1.0f - alpha) * (*(uint32 *)pixel)); |
|
|
|
win32DrawSoundBufferMarker(screenBuffer, soundOutput, pxPerSoundBufferEntry, padX, top, bottom, thisMarker->playCursor, newPixel); |
|
|
|
win32DrawSoundBufferMarker(screenBuffer, soundOutput, pxPerSoundBufferEntry, padX, top, bottom, thisMarker->writeCursor, 0x00FF0000); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@@ -456,7 +460,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.latencySampleCount = framesOfAudioLatency * (soundOutput.samplesPerSecond / gameUpdateHz); |
|
|
|
soundOutput.safetyBytes = (soundOutput.samplesPerSecond * soundOutput.bytesPerSample / gameUpdateHz) / 3; |
|
|
|
|
|
|
|
int16 *samples = (int16*)VirtualAlloc(NULL, soundOutput.secondaryBufferSize, MEM_COMMIT, PAGE_READWRITE); |
|
|
|
|
|
|
@@ -478,11 +482,12 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin |
|
|
|
globalSecondaryBuffer->Play(0, 0, DSBPLAY_LOOPING); |
|
|
|
|
|
|
|
LARGE_INTEGER lastWorkCounter = win32GetWallClock(); |
|
|
|
LARGE_INTEGER flipWallClock = win32GetWallClock(); |
|
|
|
|
|
|
|
int64 lastCycleCount = __rdtsc(); |
|
|
|
|
|
|
|
bool32 soundIsValid = false; |
|
|
|
DWORD lastPlayCursor = 0; |
|
|
|
DWORD audioLatencyBytes = 0; |
|
|
|
real32 audioLatencySeconds = 0; |
|
|
|
|
|
|
|
while (globalRunning) { |
|
|
|
GameControllerInput *oldKeyboardController = &oldInput->controllers[0]; |
|
|
@@ -561,33 +566,75 @@ 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); |
|
|
|
|
|
|
|
DWORD playCursor = 0; |
|
|
|
DWORD writeCursor = 0; |
|
|
|
bool soundIsValid = false; |
|
|
|
|
|
|
|
if (SUCCEEDED(globalSecondaryBuffer->GetCurrentPosition(&playCursor, &writeCursor))) { |
|
|
|
if (!soundIsValid) { |
|
|
|
soundOutput.runningSampleIndex = writeCursor / soundOutput.bytesPerSample; |
|
|
|
soundIsValid = true; |
|
|
|
} |
|
|
|
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; |
|
|
|
|
|
|
|
DWORD targetCursor = 0; |
|
|
|
if (audioIsLowLatency) { |
|
|
|
targetCursor = expectedFrameBoundaryByte + expectedSoundBytesPerFrame; |
|
|
|
} else { |
|
|
|
targetCursor = writeCursor + expectedSoundBytesPerFrame + soundOutput.safetyBytes; |
|
|
|
} |
|
|
|
targetCursor %= soundOutput.secondaryBufferSize; |
|
|
|
|
|
|
|
// Sound test |
|
|
|
DWORD byteToLock = 0; |
|
|
|
DWORD targetCursor = lastPlayCursor; |
|
|
|
DWORD bytesToWrite = 0; |
|
|
|
if (soundIsValid) { |
|
|
|
byteToLock = (soundOutput.runningSampleIndex*soundOutput.bytesPerSample) % soundOutput.secondaryBufferSize; |
|
|
|
targetCursor = (lastPlayCursor + soundOutput.latencySampleCount*soundOutput.bytesPerSample) % soundOutput.secondaryBufferSize; |
|
|
|
if (byteToLock == targetCursor) { |
|
|
|
bytesToWrite = 0; |
|
|
|
} else if (byteToLock > targetCursor) { |
|
|
|
DWORD bytesToWrite = 0; |
|
|
|
if (byteToLock > targetCursor) { |
|
|
|
bytesToWrite = soundOutput.secondaryBufferSize - byteToLock + targetCursor; |
|
|
|
} else { |
|
|
|
bytesToWrite = targetCursor - byteToLock; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
GameSoundOutputBuffer soundBuffer = {}; |
|
|
|
soundBuffer.samplesPerSecond = soundOutput.samplesPerSecond; |
|
|
|
soundBuffer.sampleCount = bytesToWrite / soundOutput.bytesPerSample; |
|
|
|
soundBuffer.samples = samples; |
|
|
|
GameSoundOutputBuffer soundBuffer = {}; |
|
|
|
soundBuffer.samplesPerSecond = soundOutput.samplesPerSecond; |
|
|
|
soundBuffer.sampleCount = bytesToWrite / soundOutput.bytesPerSample; |
|
|
|
soundBuffer.samples = samples; |
|
|
|
gameGetSoundSamples(&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); |
|
|
|
|
|
|
|
gameUpdateAndRender(&gameMemory, &videoBuffer, newInput, &soundBuffer); |
|
|
|
if (soundIsValid) { |
|
|
|
if (debugTimeMarkerIndex >= ArrayCount(debugMarkers)) { |
|
|
|
debugTimeMarkerIndex = 0; |
|
|
|
} |
|
|
|
|
|
|
|
globalSecondaryBuffer->GetCurrentPosition(&marker->playCursor, &marker->writeCursor); |
|
|
|
#endif |
|
|
|
win32FillSoundBuffer(&soundOutput, byteToLock, bytesToWrite, &soundBuffer); |
|
|
|
} else { |
|
|
|
soundIsValid = false; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
real32 secondsElapsedForFrame = win32GetSecondsElapsed(lastWorkCounter, win32GetWallClock()); |
|
|
|
if (secondsElapsedForFrame < targetSecondsPerFrame) { |
|
|
|
if (sleepIsGranular) { |
|
|
@@ -610,32 +657,7 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin |
|
|
|
#endif |
|
|
|
|
|
|
|
win32DrawBufferInWindow(&globalBackBuffer, window); |
|
|
|
|
|
|
|
DWORD playCursor = 0; |
|
|
|
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 |
|
|
|
flipWallClock = win32GetWallClock(); |
|
|
|
|
|
|
|
#if 0 |
|
|
|
real64 msElapsed = (real32)(1000.0f*secondsElapsedForFrame) / globalPerfCountFrequency; |
|
|
|