From 1ae8ff6dedce4c08252bd0398a528c0cfbe24b2c Mon Sep 17 00:00:00 2001 From: Raymaekers Luca Date: Sun, 28 Sep 2025 13:22:24 +0200 Subject: checkpoint --- code/win32_handmade.cpp | 1580 ----------------------------------------------- 1 file changed, 1580 deletions(-) delete mode 100755 code/win32_handmade.cpp (limited to 'code/win32_handmade.cpp') diff --git a/code/win32_handmade.cpp b/code/win32_handmade.cpp deleted file mode 100755 index 0474848..0000000 --- a/code/win32_handmade.cpp +++ /dev/null @@ -1,1580 +0,0 @@ -/* ======================================================================== - $File: $ - $Date: $ - $Revision: $ - $Creator: Casey Muratori $ - $Notice: (C) Copyright 2014 by Molly Rocket, Inc. All Rights Reserved. $ - ======================================================================== */ - -/* - TODO(casey): THIS IS NOT A FINAL PLATFORM LAYER!!! - - - Saved game locations - - Getting a handle to our own executable file - - Asset loading path - - Threading (launch a thread) - - Raw Input (support for multiple keyboards) - - Sleep/timeBeginPeriod - - ClipCursor() (for multimonitor support) - - Fullscreen support - - WM_SETCURSOR (control cursor visibility) - - QueryCancelAutoplay - - WM_ACTIVATEAPP (for when we are not the active application) - - Blit speed improvements (BitBlt) - - Hardware acceleration (OpenGL or Direct3D or BOTH??) - - GetKeyboardLayout (for French keyboards, international WASD support) - - Just a partial list of stuff!! -*/ - -#include "handmade.h" - -#include -#include -#include -#include -#include - -#include "win32_handmade.h" - -// TODO(casey): This is a global for now. -global_variable b32 GlobalRunning; -global_variable b32 GlobalPause; -global_variable win32_offscreen_buffer GlobalBackbuffer; -global_variable LPDIRECTSOUNDBUFFER GlobalSecondaryBuffer; -global_variable s64 GlobalPerfCountFrequency; - -// NOTE(casey): XInputGetState -#define X_INPUT_GET_STATE(name) DWORD WINAPI name(DWORD dwUserIndex, XINPUT_STATE *pState) -typedef X_INPUT_GET_STATE(x_input_get_state); -X_INPUT_GET_STATE(XInputGetStateStub) -{ - return(ERROR_DEVICE_NOT_CONNECTED); -} -global_variable x_input_get_state *XInputGetState_ = XInputGetStateStub; -#define XInputGetState XInputGetState_ - -// NOTE(casey): XInputSetState -#define X_INPUT_SET_STATE(name) DWORD WINAPI name(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration) -typedef X_INPUT_SET_STATE(x_input_set_state); -X_INPUT_SET_STATE(XInputSetStateStub) -{ - return(ERROR_DEVICE_NOT_CONNECTED); -} -global_variable x_input_set_state *XInputSetState_ = XInputSetStateStub; -#define XInputSetState XInputSetState_ - -#define DIRECT_SOUND_CREATE(name) HRESULT WINAPI name(LPCGUID pcGuidDevice, LPDIRECTSOUND *ppDS, LPUNKNOWN pUnkOuter) -typedef DIRECT_SOUND_CREATE(direct_sound_create); - -internal void -CatStrings(size_t SourceACount, char *SourceA, - size_t SourceBCount, char *SourceB, - size_t DestCount, char *Dest) -{ - // TODO(casey): Dest bounds checking! - - for(int Index = 0; - Index < SourceACount; - ++Index) - { - *Dest++ = *SourceA++; - } - - for(int Index = 0; - Index < SourceBCount; - ++Index) - { - *Dest++ = *SourceB++; - } - - *Dest++ = 0; -} - -internal void -Win32GetEXEFileName(win32_state *State) -{ - // NOTE(casey): Never use MAX_PATH in code that is user-facing, because it - // can be dangerous and lead to bad results. - DWORD SizeOfFilename = GetModuleFileNameA(0, State->EXEFileName, sizeof(State->EXEFileName)); - State->OnePastLastEXEFileNameSlash = State->EXEFileName; - for(char *Scan = State->EXEFileName; - *Scan; - ++Scan) - { - if(*Scan == '\\') - { - State->OnePastLastEXEFileNameSlash = Scan + 1; - } - } -} - -internal int -StringLength(char *String) -{ - int Count = 0; - while(*String++) - { - ++Count; - } - return(Count); -} - -internal void -Win32BuildEXEPathFileName(win32_state *State, char *FileName, - int DestCount, char *Dest) -{ - CatStrings(State->OnePastLastEXEFileNameSlash - State->EXEFileName, State->EXEFileName, - StringLength(FileName), FileName, - DestCount, Dest); -} - -DEBUG_PLATFORM_FREE_FILE_MEMORY(DEBUGPlatformFreeFileMemory) -{ - if(Memory) - { - VirtualFree(Memory, 0, MEM_RELEASE); - } -} - -DEBUG_PLATFORM_READ_ENTIRE_FILE(DEBUGPlatformReadEntireFile) -{ - debug_read_file_result Result = {}; - - HANDLE FileHandle = CreateFileA(FileName, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); - if(FileHandle != INVALID_HANDLE_VALUE) - { - LARGE_INTEGER FileSize; - if(GetFileSizeEx(FileHandle, &FileSize)) - { - u32 FileSize32 = SafeTruncateUInt64(FileSize.QuadPart); - Result.Contents = VirtualAlloc(0, FileSize32, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); - if(Result.Contents) - { - DWORD BytesRead; - if(ReadFile(FileHandle, Result.Contents, FileSize32, &BytesRead, 0) && - (FileSize32 == BytesRead)) - { - // NOTE(casey): File read successfully - Result.ContentsSize = FileSize32; - } - else - { - // TODO(casey): Logging - DEBUGPlatformFreeFileMemory(Thread, Result.Contents, Result.ContentsSize); - Result.Contents = 0; - } - } - else - { - // TODO(casey): Logging - } - } - else - { - // TODO(casey): Logging - } - - CloseHandle(FileHandle); - } - else - { - // TODO(casey): Logging - } - - return(Result); -} - -DEBUG_PLATFORM_WRITE_ENTIRE_FILE(DEBUGPlatformWriteEntireFile) -{ - b32 Result = false; - - HANDLE FileHandle = CreateFileA(FileName, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0); - if(FileHandle != INVALID_HANDLE_VALUE) - { - DWORD BytesWritten; - if(WriteFile(FileHandle, Memory, MemorySize, &BytesWritten, 0)) - { - // NOTE(casey): File read successfully - Result = (BytesWritten == MemorySize); - } - else - { - // TODO(casey): Logging - } - - CloseHandle(FileHandle); - } - else - { - // TODO(casey): Logging - } - - return(Result); -} - -inline FILETIME -Win32GetLastWriteTime(char *Filename) -{ - FILETIME LastWriteTime = {}; - - WIN32_FILE_ATTRIBUTE_DATA Data; - if(GetFileAttributesEx(Filename, GetFileExInfoStandard, &Data)) - { - LastWriteTime = Data.ftLastWriteTime; - } - - return(LastWriteTime); -} - -internal win32_game_code -Win32LoadGameCode(char *SourceDLLName, char *TempDLLName) -{ - win32_game_code Result = {}; - - // TODO(casey): Need to get the proper path here! - // TODO(casey): Automatic determination of when updates are necessary. - - Result.DLLLastWriteTime = Win32GetLastWriteTime(SourceDLLName); - - CopyFile(SourceDLLName, TempDLLName, FALSE); - - Result.GameCodeDLL = LoadLibraryA(TempDLLName); - if(Result.GameCodeDLL) - { - Result.UpdateAndRender = (game_update_and_render *) - GetProcAddress(Result.GameCodeDLL, "GameUpdateAndRender"); - - Result.GetSoundSamples = (game_get_sound_samples *) - GetProcAddress(Result.GameCodeDLL, "GameGetSoundSamples"); - - Result.IsValid = (Result.UpdateAndRender && - Result.GetSoundSamples); - } - - if(!Result.IsValid) - { - Result.UpdateAndRender = 0; - Result.GetSoundSamples = 0; - } - - return(Result); -} - -internal void -Win32UnloadGameCode(win32_game_code *GameCode) -{ - if(GameCode->GameCodeDLL) - { - FreeLibrary(GameCode->GameCodeDLL); - GameCode->GameCodeDLL = 0; - } - - GameCode->IsValid = false; - GameCode->UpdateAndRender = 0; - GameCode->GetSoundSamples = 0; -} - -internal void -Win32LoadXInput(void) -{ - // TODO(casey): Test this on Windows 8 - HMODULE XInputLibrary = LoadLibraryA("xinput1_4.dll"); - if(!XInputLibrary) - { - // TODO(casey): Diagnostic - XInputLibrary = LoadLibraryA("xinput9_1_0.dll"); - } - - if(!XInputLibrary) - { - // TODO(casey): Diagnostic - XInputLibrary = LoadLibraryA("xinput1_3.dll"); - } - - if(XInputLibrary) - { - XInputGetState = (x_input_get_state *)GetProcAddress(XInputLibrary, "XInputGetState"); - if(!XInputGetState) {XInputGetState = XInputGetStateStub;} - - XInputSetState = (x_input_set_state *)GetProcAddress(XInputLibrary, "XInputSetState"); - if(!XInputSetState) {XInputSetState = XInputSetStateStub;} - - // TODO(casey): Diagnostic - - } - else - { - // TODO(casey): Diagnostic - } -} - -internal void -Win32InitDSound(HWND Window, s32 SamplesPerSecond, s32 BufferSize) -{ - // NOTE(casey): Load the library - HMODULE DSoundLibrary = LoadLibraryA("dsound.dll"); - if(DSoundLibrary) - { - // NOTE(casey): Get a DirectSound object! - cooperative - direct_sound_create *DirectSoundCreate = (direct_sound_create *) - GetProcAddress(DSoundLibrary, "DirectSoundCreate"); - - // TODO(casey): Double-check that this works on XP - DirectSound8 or 7?? - LPDIRECTSOUND DirectSound; - if(DirectSoundCreate && SUCCEEDED(DirectSoundCreate(0, &DirectSound, 0))) - { - WAVEFORMATEX WaveFormat = {}; - WaveFormat.wFormatTag = WAVE_FORMAT_PCM; - WaveFormat.nChannels = 2; - WaveFormat.nSamplesPerSec = SamplesPerSecond; - WaveFormat.wBitsPerSample = 16; - WaveFormat.nBlockAlign = (WaveFormat.nChannels*WaveFormat.wBitsPerSample) / 8; - WaveFormat.nAvgBytesPerSec = WaveFormat.nSamplesPerSec*WaveFormat.nBlockAlign; - WaveFormat.cbSize = 0; - - if(SUCCEEDED(DirectSound->SetCooperativeLevel(Window, DSSCL_PRIORITY))) - { - DSBUFFERDESC BufferDescription = {}; - BufferDescription.dwSize = sizeof(BufferDescription); - BufferDescription.dwFlags = DSBCAPS_PRIMARYBUFFER; - - // NOTE(casey): "Create" a primary buffer - // TODO(casey): DSBCAPS_GLOBALFOCUS? - LPDIRECTSOUNDBUFFER PrimaryBuffer; - if(SUCCEEDED(DirectSound->CreateSoundBuffer(&BufferDescription, &PrimaryBuffer, 0))) - { - HRESULT Error = PrimaryBuffer->SetFormat(&WaveFormat); - if(SUCCEEDED(Error)) - { - // NOTE(casey): We have finally set the format! - OutputDebugStringA("Primary buffer format was set.\n"); - } - else - { - // TODO(casey): Diagnostic - } - } - else - { - // TODO(casey): Diagnostic - } - } - else - { - // TODO(casey): Diagnostic - } - - // TODO(casey): DSBCAPS_GETCURRENTPOSITION2 - DSBUFFERDESC BufferDescription = {}; - BufferDescription.dwSize = sizeof(BufferDescription); - BufferDescription.dwFlags = DSBCAPS_GETCURRENTPOSITION2; - BufferDescription.dwBufferBytes = BufferSize; - BufferDescription.lpwfxFormat = &WaveFormat; - HRESULT Error = DirectSound->CreateSoundBuffer(&BufferDescription, &GlobalSecondaryBuffer, 0); - if(SUCCEEDED(Error)) - { - OutputDebugStringA("Secondary buffer created successfully.\n"); - } - } - else - { - // TODO(casey): Diagnostic - } - } - else - { - // TODO(casey): Diagnostic - } -} - -internal win32_window_dimension -Win32GetWindowDimension(HWND Window) -{ - win32_window_dimension Result; - - RECT ClientRect; - GetClientRect(Window, &ClientRect); - Result.Width = ClientRect.right - ClientRect.left; - Result.Height = ClientRect.bottom - ClientRect.top; - - return(Result); -} - -internal void -Win32ResizeDIBSection(win32_offscreen_buffer *Buffer, int Width, int Height) -{ - // TODO(casey): Bulletproof this. - // Maybe don't free first, free after, then free first if that fails. - - if(Buffer->Memory) - { - VirtualFree(Buffer->Memory, 0, MEM_RELEASE); - } - - Buffer->Width = Width; - Buffer->Height = Height; - - int BytesPerPixel = 4; - Buffer->BytesPerPixel = BytesPerPixel; - - // NOTE(casey): When the biHeight field is negative, this is the clue to - // Windows to treat this bitmap as top-down, not bottom-up, meaning that - // the first three bytes of the image are the color for the top left pixel - // in the bitmap, not the bottom left! - Buffer->Info.bmiHeader.biSize = sizeof(Buffer->Info.bmiHeader); - Buffer->Info.bmiHeader.biWidth = Buffer->Width; - Buffer->Info.bmiHeader.biHeight = -Buffer->Height; - Buffer->Info.bmiHeader.biPlanes = 1; - Buffer->Info.bmiHeader.biBitCount = 32; - Buffer->Info.bmiHeader.biCompression = BI_RGB; - - // NOTE(casey): Thank you to Chris Hecker of Spy Party fame - // for clarifying the deal with StretchDIBits and BitBlt! - // No more DC for us. - int BitmapMemorySize = (Buffer->Width*Buffer->Height)*BytesPerPixel; - Buffer->Memory = VirtualAlloc(0, BitmapMemorySize, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); - Buffer->Pitch = Width*BytesPerPixel; - - // TODO(casey): Probably clear this to black -} - -internal void -Win32DisplayBufferInWindow(win32_offscreen_buffer *Buffer, - HDC DeviceContext, int WindowWidth, int WindowHeight) -{ - // NOTE(casey): For prototyping purposes, we're going to always blit - // 1-to-1 pixels to make sure we don't introduce artifacts with - // stretching while we are learning to code the renderer! - StretchDIBits(DeviceContext, - 0, 0, Buffer->Width, Buffer->Height, - 0, 0, Buffer->Width, Buffer->Height, - Buffer->Memory, - &Buffer->Info, - DIB_RGB_COLORS, SRCCOPY); -} - -internal LRESULT CALLBACK -Win32MainWindowCallback(HWND Window, - UINT Message, - WPARAM WParam, - LPARAM LParam) -{ - LRESULT Result = 0; - - switch(Message) - { - case WM_CLOSE: - { - // TODO(casey): Handle this with a message to the user? - GlobalRunning = false; - } break; - - case WM_ACTIVATEAPP: - { -#if 0 - if(WParam == TRUE) - { - SetLayeredWindowAttributes(Window, RGB(0, 0, 0), 255, LWA_ALPHA); - } - else - { - SetLayeredWindowAttributes(Window, RGB(0, 0, 0), 64, LWA_ALPHA); - } -#endif - } break; - - case WM_DESTROY: - { - // TODO(casey): Handle this as an error - recreate window? - GlobalRunning = false; - } break; - - case WM_SYSKEYDOWN: - case WM_SYSKEYUP: - case WM_KEYDOWN: - case WM_KEYUP: - { - Assert(!"Keyboard input came in through a non-dispatch message!"); - } break; - - case WM_PAINT: - { - PAINTSTRUCT Paint; - HDC DeviceContext = BeginPaint(Window, &Paint); - win32_window_dimension Dimension = Win32GetWindowDimension(Window); - Win32DisplayBufferInWindow(&GlobalBackbuffer, DeviceContext, - Dimension.Width, Dimension.Height); - EndPaint(Window, &Paint); - } break; - - default: - { - // OutputDebugStringA("default\n"); - Result = DefWindowProcA(Window, Message, WParam, LParam); - } break; - } - - return(Result); -} - -internal void -Win32ClearBuffer(win32_sound_output *SoundOutput) -{ - VOID *Region1; - DWORD Region1Size; - VOID *Region2; - DWORD Region2Size; - if(SUCCEEDED(GlobalSecondaryBuffer->Lock(0, SoundOutput->SecondaryBufferSize, - &Region1, &Region1Size, - &Region2, &Region2Size, - 0))) - { - // TODO(casey): assert that Region1Size/Region2Size is valid - u8 *DestSample = (u8 *)Region1; - for(DWORD ByteIndex = 0; - ByteIndex < Region1Size; - ++ByteIndex) - { - *DestSample++ = 0; - } - - DestSample = (u8 *)Region2; - for(DWORD ByteIndex = 0; - ByteIndex < Region2Size; - ++ByteIndex) - { - *DestSample++ = 0; - } - - GlobalSecondaryBuffer->Unlock(Region1, Region1Size, Region2, Region2Size); - } -} - -internal void -Win32FillSoundBuffer(win32_sound_output *SoundOutput, DWORD ByteToLock, DWORD BytesToWrite, - game_sound_output_buffer *SourceBuffer) -{ - // TODO(casey): More strenuous test! - VOID *Region1; - DWORD Region1Size; - VOID *Region2; - DWORD Region2Size; - if(SUCCEEDED(GlobalSecondaryBuffer->Lock(ByteToLock, BytesToWrite, - &Region1, &Region1Size, - &Region2, &Region2Size, - 0))) - { - // TODO(casey): assert that Region1Size/Region2Size is valid - - // TODO(casey): Collapse these two loops - DWORD Region1SampleCount = Region1Size/SoundOutput->BytesPerSample; - s16 *DestSample = (s16 *)Region1; - s16 *SourceSample = SourceBuffer->Samples; - for(DWORD SampleIndex = 0; - SampleIndex < Region1SampleCount; - ++SampleIndex) - { - *DestSample++ = *SourceSample++; - *DestSample++ = *SourceSample++; - ++SoundOutput->RunningSampleIndex; - } - - DWORD Region2SampleCount = Region2Size/SoundOutput->BytesPerSample; - DestSample = (s16 *)Region2; - for(DWORD SampleIndex = 0; - SampleIndex < Region2SampleCount; - ++SampleIndex) - { - *DestSample++ = *SourceSample++; - *DestSample++ = *SourceSample++; - ++SoundOutput->RunningSampleIndex; - } - - GlobalSecondaryBuffer->Unlock(Region1, Region1Size, Region2, Region2Size); - } -} - -internal void -Win32ProcessKeyboardMessage(game_button_state *NewState, b32 IsDown) -{ - if(NewState->EndedDown != IsDown) - { - NewState->EndedDown = IsDown; - ++NewState->HalfTransitionCount; - } -} - -internal void -Win32ProcessXInputDigitalButton(DWORD XInputButtonState, - game_button_state *OldState, DWORD ButtonBit, - game_button_state *NewState) -{ - NewState->EndedDown = ((XInputButtonState & ButtonBit) == ButtonBit); - NewState->HalfTransitionCount = (OldState->EndedDown != NewState->EndedDown) ? 1 : 0; -} - -internal r32 -Win32ProcessXInputStickValue(SHORT Value, SHORT DeadZoneThreshold) -{ - r32 Result = 0; - - if(Value < -DeadZoneThreshold) - { - Result = (r32)((Value + DeadZoneThreshold) / (32768.0f - DeadZoneThreshold)); - } - else if(Value > DeadZoneThreshold) - { - Result = (r32)((Value - DeadZoneThreshold) / (32767.0f - DeadZoneThreshold)); - } - - return(Result); -} - -internal void -Win32GetInputFileLocation(win32_state *State, b32 InputStream, - int SlotIndex, int DestCount, char *Dest) -{ - char Temp[64]; - wsprintf(Temp, "loop_edit_%d_%s.hmi", SlotIndex, InputStream ? "input" : "state"); - Win32BuildEXEPathFileName(State, Temp, DestCount, Dest); -} - -internal win32_replay_buffer * -Win32GetReplayBuffer(win32_state *State, int unsigned Index) -{ - Assert(Index < ArrayCount(State->ReplayBuffers)); - win32_replay_buffer *Result = &State->ReplayBuffers[Index]; - return(Result); -} - -internal void -Win32BeginRecordingInput(win32_state *State, int InputRecordingIndex) -{ - win32_replay_buffer *ReplayBuffer = Win32GetReplayBuffer(State, InputRecordingIndex); - if(ReplayBuffer->MemoryBlock) - { - State->InputRecordingIndex = InputRecordingIndex; - - char FileName[WIN32_STATE_FILE_NAME_COUNT]; - Win32GetInputFileLocation(State, true, InputRecordingIndex, sizeof(FileName), FileName); - State->RecordingHandle = CreateFileA(FileName, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0); - -#if 0 - LARGE_INTEGER FilePosition; - FilePosition.QuadPart = State->TotalSize; - SetFilePointerEx(State->RecordingHandle, FilePosition, 0, FILE_BEGIN); -#endif - - CopyMemory(ReplayBuffer->MemoryBlock, State->GameMemoryBlock, State->TotalSize); - } -} - -internal void -Win32EndRecordingInput(win32_state *State) -{ - CloseHandle(State->RecordingHandle); - State->InputRecordingIndex = 0; -} - -internal void -Win32BeginInputPlayBack(win32_state *State, int InputPlayingIndex) -{ - win32_replay_buffer *ReplayBuffer = Win32GetReplayBuffer(State, InputPlayingIndex); - if(ReplayBuffer->MemoryBlock) - { - State->InputPlayingIndex = InputPlayingIndex; - - char FileName[WIN32_STATE_FILE_NAME_COUNT]; - Win32GetInputFileLocation(State, true, InputPlayingIndex, sizeof(FileName), FileName); - State->PlaybackHandle = CreateFileA(FileName, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0); - -#if 0 - LARGE_INTEGER FilePosition; - FilePosition.QuadPart = State->TotalSize; - SetFilePointerEx(State->PlaybackHandle, FilePosition, 0, FILE_BEGIN); -#endif - - CopyMemory(State->GameMemoryBlock, ReplayBuffer->MemoryBlock, State->TotalSize); - } -} - -internal void -Win32EndInputPlayBack(win32_state *State) -{ - CloseHandle(State->PlaybackHandle); - State->InputPlayingIndex = 0; -} - -internal void -Win32RecordInput(win32_state *State, game_input *NewInput) -{ - DWORD BytesWritten; - WriteFile(State->RecordingHandle, NewInput, sizeof(*NewInput), &BytesWritten, 0); -} - -internal void -Win32PlayBackInput(win32_state *State, game_input *NewInput) -{ - DWORD BytesRead = 0; - if(ReadFile(State->PlaybackHandle, NewInput, sizeof(*NewInput), &BytesRead, 0)) - { - if(BytesRead == 0) - { - // NOTE(casey): We've hit the end of the stream, go back to the beginning - int PlayingIndex = State->InputPlayingIndex; - Win32EndInputPlayBack(State); - Win32BeginInputPlayBack(State, PlayingIndex); - ReadFile(State->PlaybackHandle, NewInput, sizeof(*NewInput), &BytesRead, 0); - } - } -} - -internal void -Win32ProcessPendingMessages(win32_state *State, game_controller_input *KeyboardController) -{ - MSG Message; - while(PeekMessage(&Message, 0, 0, 0, PM_REMOVE)) - { - switch(Message.message) - { - case WM_QUIT: - { - GlobalRunning = false; - } break; - - case WM_SYSKEYDOWN: - case WM_SYSKEYUP: - case WM_KEYDOWN: - case WM_KEYUP: - { - u32 VKCode = (u32)Message.wParam; - - // NOTE(casey): Since we are comparing WasDown to IsDown, - // we MUST use == and != to convert these bit tests to actual - // 0 or 1 values. - b32 WasDown = ((Message.lParam & (1 << 30)) != 0); - b32 IsDown = ((Message.lParam & (1 << 31)) == 0); - if(WasDown != IsDown) - { - if(VKCode == 'W') - { - Win32ProcessKeyboardMessage(&KeyboardController->MoveUp, IsDown); - } - else if(VKCode == 'A') - { - Win32ProcessKeyboardMessage(&KeyboardController->MoveLeft, IsDown); - } - else if(VKCode == 'R') - { - Win32ProcessKeyboardMessage(&KeyboardController->MoveDown, IsDown); - } - else if(VKCode == 'S') - { - Win32ProcessKeyboardMessage(&KeyboardController->MoveRight, IsDown); - } - else if(VKCode == 'Q') - { - Win32ProcessKeyboardMessage(&KeyboardController->LeftShoulder, IsDown); - } - else if(VKCode == 'E') - { - Win32ProcessKeyboardMessage(&KeyboardController->RightShoulder, IsDown); - } - else if(VKCode == VK_UP) - { - Win32ProcessKeyboardMessage(&KeyboardController->ActionUp, IsDown); - } - else if(VKCode == VK_LEFT) - { - Win32ProcessKeyboardMessage(&KeyboardController->ActionLeft, IsDown); - } - else if(VKCode == VK_DOWN) - { - Win32ProcessKeyboardMessage(&KeyboardController->ActionDown, IsDown); - } - else if(VKCode == VK_RIGHT) - { - Win32ProcessKeyboardMessage(&KeyboardController->ActionRight, IsDown); - } - else if(VKCode == VK_ESCAPE) - { - Win32ProcessKeyboardMessage(&KeyboardController->Start, IsDown); - } - else if(VKCode == VK_SPACE) - { - Win32ProcessKeyboardMessage(&KeyboardController->Back, IsDown); - } -#if HANDMADE_INTERNAL - else if(VKCode == 'P') - { - if(IsDown) - { - GlobalPause = !GlobalPause; - } - } - else if(VKCode == 'L') - { - if(IsDown) - { - if(State->InputPlayingIndex == 0) - { - if(State->InputRecordingIndex == 0) - { - Win32BeginRecordingInput(State, 1); - } - else - { - Win32EndRecordingInput(State); - Win32BeginInputPlayBack(State, 1); - } - } - else - { - Win32EndInputPlayBack(State); - } - } - } -#endif - } - - b32 AltKeyWasDown = (Message.lParam & (1 << 29)); - if((VKCode == VK_F4) && AltKeyWasDown) - { - GlobalRunning = false; - } - } break; - - default: - { - TranslateMessage(&Message); - DispatchMessageA(&Message); - } break; - } - } -} - -inline LARGE_INTEGER -Win32GetWallClock(void) -{ - LARGE_INTEGER Result; - QueryPerformanceCounter(&Result); - return(Result); -} - -inline r32 -Win32GetSecondsElapsed(LARGE_INTEGER Start, LARGE_INTEGER End) -{ - r32 Result = ((r32)(End.QuadPart - Start.QuadPart) / - (r32)GlobalPerfCountFrequency); - return(Result); -} - -#if 0 - -internal void -Win32DebugDrawVertical(win32_offscreen_buffer *Backbuffer, - int X, int Top, int Bottom, u32 Color) -{ - if(Top <= 0) - { - Top = 0; - } - - if(Bottom > Backbuffer->Height) - { - Bottom = Backbuffer->Height; - } - - if((X >= 0) && (X < Backbuffer->Width)) - { - u8 *Pixel = ((u8 *)Backbuffer->Memory + - X*Backbuffer->BytesPerPixel + - Top*Backbuffer->Pitch); - for(int Y = Top; - Y < Bottom; - ++Y) - { - *(u32 *)Pixel = Color; - Pixel += Backbuffer->Pitch; - } - } -} - -inline void -Win32DrawSoundBufferMarker(win32_offscreen_buffer *Backbuffer, - win32_sound_output *SoundOutput, - r32 C, int PadX, int Top, int Bottom, - DWORD Value, u32 Color) -{ - r32 XReal32 = (C * (r32)Value); - int X = PadX + (int)XReal32; - Win32DebugDrawVertical(Backbuffer, X, Top, Bottom, Color); -} - -internal void -Win32DebugSyncDisplay(win32_offscreen_buffer *Backbuffer, - int MarkerCount, win32_debug_time_marker *Markers, - int CurrentMarkerIndex, - win32_sound_output *SoundOutput, r32 TargetSecondsPerFrame) -{ - int PadX = 16; - int PadY = 16; - - int LineHeight = 64; - - r32 C = (r32)(Backbuffer->Width - 2*PadX) / (r32)SoundOutput->SecondaryBufferSize; - for(int MarkerIndex = 0; - MarkerIndex < MarkerCount; - ++MarkerIndex) - { - win32_debug_time_marker *ThisMarker = &Markers[MarkerIndex]; - Assert(ThisMarker->OutputPlayCursor < SoundOutput->SecondaryBufferSize); - Assert(ThisMarker->OutputWriteCursor < SoundOutput->SecondaryBufferSize); - Assert(ThisMarker->OutputLocation < SoundOutput->SecondaryBufferSize); - Assert(ThisMarker->OutputByteCount < SoundOutput->SecondaryBufferSize); - Assert(ThisMarker->FlipPlayCursor < SoundOutput->SecondaryBufferSize); - Assert(ThisMarker->FlipWriteCursor < SoundOutput->SecondaryBufferSize); - - DWORD PlayColor = 0xFFFFFFFF; - DWORD WriteColor = 0xFFFF0000; - DWORD ExpectedFlipColor = 0xFFFFFF00; - DWORD PlayWindowColor = 0xFFFF00FF; - - int Top = PadY; - int Bottom = PadY + LineHeight; - if(MarkerIndex == CurrentMarkerIndex) - { - Top += LineHeight+PadY; - Bottom += LineHeight+PadY; - - int FirstTop = Top; - - Win32DrawSoundBufferMarker(Backbuffer, SoundOutput, C, PadX, Top, Bottom, ThisMarker->OutputPlayCursor, PlayColor); - Win32DrawSoundBufferMarker(Backbuffer, SoundOutput, C, PadX, Top, Bottom, ThisMarker->OutputWriteCursor, WriteColor); - - Top += LineHeight+PadY; - Bottom += LineHeight+PadY; - - Win32DrawSoundBufferMarker(Backbuffer, SoundOutput, C, PadX, Top, Bottom, ThisMarker->OutputLocation, PlayColor); - Win32DrawSoundBufferMarker(Backbuffer, SoundOutput, C, PadX, Top, Bottom, ThisMarker->OutputLocation + ThisMarker->OutputByteCount, WriteColor); - - Top += LineHeight+PadY; - Bottom += LineHeight+PadY; - - Win32DrawSoundBufferMarker(Backbuffer, SoundOutput, C, PadX, FirstTop, Bottom, ThisMarker->ExpectedFlipPlayCursor, ExpectedFlipColor); - } - - Win32DrawSoundBufferMarker(Backbuffer, SoundOutput, C, PadX, Top, Bottom, ThisMarker->FlipPlayCursor, PlayColor); - Win32DrawSoundBufferMarker(Backbuffer, SoundOutput, C, PadX, Top, Bottom, ThisMarker->FlipPlayCursor + 480*SoundOutput->BytesPerSample, PlayWindowColor); - Win32DrawSoundBufferMarker(Backbuffer, SoundOutput, C, PadX, Top, Bottom, ThisMarker->FlipWriteCursor, WriteColor); - } -} - -#endif - -int CALLBACK -WinMain(HINSTANCE Instance, - HINSTANCE PrevInstance, - LPSTR CommandLine, - int ShowCode) -{ - win32_state Win32State = {}; - - LARGE_INTEGER PerfCountFrequencyResult; - QueryPerformanceFrequency(&PerfCountFrequencyResult); - GlobalPerfCountFrequency = PerfCountFrequencyResult.QuadPart; - - Win32GetEXEFileName(&Win32State); - - char SourceGameCodeDLLFullPath[WIN32_STATE_FILE_NAME_COUNT]; - Win32BuildEXEPathFileName(&Win32State, "handmade.dll", - sizeof(SourceGameCodeDLLFullPath), SourceGameCodeDLLFullPath); - - char TempGameCodeDLLFullPath[WIN32_STATE_FILE_NAME_COUNT]; - Win32BuildEXEPathFileName(&Win32State, "handmade_temp.dll", - sizeof(TempGameCodeDLLFullPath), TempGameCodeDLLFullPath); - - // NOTE(casey): Set the Windows scheduler granularity to 1ms - // so that our Sleep() can be more granular. - UINT DesiredSchedulerMS = 1; - b32 SleepIsGranular = (timeBeginPeriod(DesiredSchedulerMS) == TIMERR_NOERROR); - - Win32LoadXInput(); - - WNDCLASSA WindowClass = {}; - - Win32ResizeDIBSection(&GlobalBackbuffer, 1280, 720); - - WindowClass.style = CS_HREDRAW|CS_VREDRAW; - WindowClass.lpfnWndProc = Win32MainWindowCallback; - WindowClass.hInstance = Instance; - // WindowClass.hIcon; - WindowClass.lpszClassName = "HandmadeHeroWindowClass"; - - if(RegisterClassA(&WindowClass)) - { - HWND Window = - CreateWindowExA( - 0, // WS_EX_TOPMOST|WS_EX_LAYERED, - WindowClass.lpszClassName, - "Handmade Hero", - WS_OVERLAPPEDWINDOW|WS_VISIBLE, - CW_USEDEFAULT, - CW_USEDEFAULT, - CW_USEDEFAULT, - CW_USEDEFAULT, - 0, - 0, - Instance, - 0); - if(Window) - { - win32_sound_output SoundOutput = {}; - - // TODO(casey): How do we reliably query on this on Windows? - int MonitorRefreshHz = 60; - HDC RefreshDC = GetDC(Window); - int Win32RefreshRate = GetDeviceCaps(RefreshDC, VREFRESH); - ReleaseDC(Window, RefreshDC); - if(Win32RefreshRate > 1) - { - MonitorRefreshHz = Win32RefreshRate; - } - r32 GameUpdateHz = (MonitorRefreshHz / 2.0f); - r32 TargetSecondsPerFrame = 1.0f / (r32)GameUpdateHz; - - // TODO(casey): Make this like sixty seconds? - SoundOutput.SamplesPerSecond = 48000; - SoundOutput.BytesPerSample = sizeof(s16)*2; - SoundOutput.SecondaryBufferSize = SoundOutput.SamplesPerSecond*SoundOutput.BytesPerSample; - // TODO(casey): Actually compute this variance and see - // what the lowest reasonable value is. - SoundOutput.SafetyBytes = (int)(((r32)SoundOutput.SamplesPerSecond*(r32)SoundOutput.BytesPerSample / GameUpdateHz)/3.0f); - Win32InitDSound(Window, SoundOutput.SamplesPerSecond, SoundOutput.SecondaryBufferSize); - Win32ClearBuffer(&SoundOutput); - GlobalSecondaryBuffer->Play(0, 0, DSBPLAY_LOOPING); - - GlobalRunning = true; - -#if 0 - // NOTE(casey): This tests the PlayCursor/WriteCursor update frequency - // On the Handmade Hero machine, it was 480 samples. - while(GlobalRunning) - { - DWORD PlayCursor; - DWORD WriteCursor; - GlobalSecondaryBuffer->GetCurrentPosition(&PlayCursor, &WriteCursor); - - char TextBuffer[256]; - _snprintf_s(TextBuffer, sizeof(TextBuffer), - "PC:%u WC:%u\n", PlayCursor, WriteCursor); - OutputDebugStringA(TextBuffer); - } -#endif - - // TODO(casey): Pool with bitmap VirtualAlloc - s16 *Samples = (s16 *)VirtualAlloc(0, SoundOutput.SecondaryBufferSize, - MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); - - -#if HANDMADE_INTERNAL - LPVOID BaseAddress = (LPVOID)Terabytes(2); -#else - LPVOID BaseAddress = 0; -#endif - - game_memory GameMemory = {}; - GameMemory.PermanentStorageSize = Megabytes(64); - GameMemory.TransientStorageSize = Gigabytes(1); - GameMemory.DEBUGPlatformFreeFileMemory = DEBUGPlatformFreeFileMemory; - GameMemory.DEBUGPlatformReadEntireFile = DEBUGPlatformReadEntireFile; - GameMemory.DEBUGPlatformWriteEntireFile = DEBUGPlatformWriteEntireFile; - - - // TODO(casey): Handle various memory footprints (USING SYSTEM METRICS) - // TODO(casey): Use MEM_LARGE_PAGES and call adjust token - // privileges when not on Windows XP? - Win32State.TotalSize = GameMemory.PermanentStorageSize + GameMemory.TransientStorageSize; - Win32State.GameMemoryBlock = VirtualAlloc(BaseAddress, (size_t)Win32State.TotalSize, - MEM_RESERVE|MEM_COMMIT, - PAGE_READWRITE); - GameMemory.PermanentStorage = Win32State.GameMemoryBlock; - GameMemory.TransientStorage = ((u8 *)GameMemory.PermanentStorage + - GameMemory.PermanentStorageSize); - - for(int ReplayIndex = 0; - ReplayIndex < ArrayCount(Win32State.ReplayBuffers); - ++ReplayIndex) - { - win32_replay_buffer *ReplayBuffer = &Win32State.ReplayBuffers[ReplayIndex]; - - // TODO(casey): Recording system still seems to take too long - // on record start - find out what Windows is doing and if - // we can speed up / defer some of that processing. - - Win32GetInputFileLocation(&Win32State, false, ReplayIndex, - sizeof(ReplayBuffer->FileName), ReplayBuffer->FileName); - - ReplayBuffer->FileHandle = - CreateFileA(ReplayBuffer->FileName, - GENERIC_WRITE|GENERIC_READ, 0, 0, CREATE_ALWAYS, 0, 0); - - LARGE_INTEGER MaxSize; - MaxSize.QuadPart = Win32State.TotalSize; - ReplayBuffer->MemoryMap = CreateFileMapping( - ReplayBuffer->FileHandle, 0, PAGE_READWRITE, - MaxSize.HighPart, MaxSize.LowPart, 0); - - ReplayBuffer->MemoryBlock = MapViewOfFile( - ReplayBuffer->MemoryMap, FILE_MAP_ALL_ACCESS, 0, 0, Win32State.TotalSize); - if(ReplayBuffer->MemoryBlock) - { - } - else - { - // TODO(casey): Diagnostic - } - } - - if(Samples && GameMemory.PermanentStorage && GameMemory.TransientStorage) - { - game_input Input[2] = {}; - game_input *NewInput = &Input[0]; - game_input *OldInput = &Input[1]; - - LARGE_INTEGER LastCounter = Win32GetWallClock(); - LARGE_INTEGER FlipWallClock = Win32GetWallClock(); - - int DebugTimeMarkerIndex = 0; - win32_debug_time_marker DebugTimeMarkers[30] = {0}; - - DWORD AudioLatencyBytes = 0; - r32 AudioLatencySeconds = 0; - b32 SoundIsValid = false; - - win32_game_code Game = Win32LoadGameCode(SourceGameCodeDLLFullPath, - TempGameCodeDLLFullPath); - u32 LoadCounter = 0; - - s64 LastCycleCount = __rdtsc(); - while(GlobalRunning) - { - FILETIME NewDLLWriteTime = Win32GetLastWriteTime(SourceGameCodeDLLFullPath); - if(CompareFileTime(&NewDLLWriteTime, &Game.DLLLastWriteTime) != 0) - { - Win32UnloadGameCode(&Game); - Game = Win32LoadGameCode(SourceGameCodeDLLFullPath, - TempGameCodeDLLFullPath); - LoadCounter = 0; - } - - // TODO(casey): Zeroing macro - // TODO(casey): We can't zero everything because the up/down state will - // be wrong!!! - game_controller_input *OldKeyboardController = GetController(OldInput, 0); - game_controller_input *NewKeyboardController = GetController(NewInput, 0); - *NewKeyboardController = {}; - NewKeyboardController->IsConnected = true; - for(int ButtonIndex = 0; - ButtonIndex < ArrayCount(NewKeyboardController->Buttons); - ++ButtonIndex) - { - NewKeyboardController->Buttons[ButtonIndex].EndedDown = - OldKeyboardController->Buttons[ButtonIndex].EndedDown; - } - - Win32ProcessPendingMessages(&Win32State, NewKeyboardController); - - if(!GlobalPause) - { - POINT MouseP; - GetCursorPos(&MouseP); - ScreenToClient(Window, &MouseP); - NewInput->MouseX = MouseP.x; - NewInput->MouseY = MouseP.y; - NewInput->MouseZ = 0; // TODO(casey): Support mousewheel? - Win32ProcessKeyboardMessage(&NewInput->MouseButtons[0], - GetKeyState(VK_LBUTTON) & (1 << 15)); - Win32ProcessKeyboardMessage(&NewInput->MouseButtons[1], - GetKeyState(VK_MBUTTON) & (1 << 15)); - Win32ProcessKeyboardMessage(&NewInput->MouseButtons[2], - GetKeyState(VK_RBUTTON) & (1 << 15)); - Win32ProcessKeyboardMessage(&NewInput->MouseButtons[3], - GetKeyState(VK_XBUTTON1) & (1 << 15)); - Win32ProcessKeyboardMessage(&NewInput->MouseButtons[4], - GetKeyState(VK_XBUTTON2) & (1 << 15)); - - // TODO(casey): Need to not poll disconnected controllers to avoid - // xinput frame rate hit on older libraries... - // TODO(casey): Should we poll this more frequently - DWORD MaxControllerCount = XUSER_MAX_COUNT; - if(MaxControllerCount > (ArrayCount(NewInput->Controllers) - 1)) - { - MaxControllerCount = (ArrayCount(NewInput->Controllers) - 1); - } - - for (DWORD ControllerIndex = 0; - ControllerIndex < MaxControllerCount; - ++ControllerIndex) - { - DWORD OurControllerIndex = ControllerIndex + 1; - game_controller_input *OldController = GetController(OldInput, OurControllerIndex); - game_controller_input *NewController = GetController(NewInput, OurControllerIndex); - - XINPUT_STATE ControllerState; - if(XInputGetState(ControllerIndex, &ControllerState) == ERROR_SUCCESS) - { - NewController->IsConnected = true; - NewController->IsAnalog = OldController->IsAnalog; - - // NOTE(casey): This controller is plugged in - // TODO(casey): See if ControllerState.dwPacketNumber increments too rapidly - XINPUT_GAMEPAD *Pad = &ControllerState.Gamepad; - - // TODO(casey): This is a square deadzone, check XInput to - // verify that the deadzone is "round" and show how to do - // round deadzone processing. - NewController->StickAverageX = Win32ProcessXInputStickValue( - Pad->sThumbLX, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE); - NewController->StickAverageY = Win32ProcessXInputStickValue( - Pad->sThumbLY, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE); - if((NewController->StickAverageX != 0.0f) || - (NewController->StickAverageY != 0.0f)) - { - NewController->IsAnalog = true; - } - - if(Pad->wButtons & XINPUT_GAMEPAD_DPAD_UP) - { - NewController->StickAverageY = 1.0f; - NewController->IsAnalog = false; - } - - if(Pad->wButtons & XINPUT_GAMEPAD_DPAD_DOWN) - { - NewController->StickAverageY = -1.0f; - NewController->IsAnalog = false; - } - - if(Pad->wButtons & XINPUT_GAMEPAD_DPAD_LEFT) - { - NewController->StickAverageX = -1.0f; - NewController->IsAnalog = false; - } - - if(Pad->wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) - { - NewController->StickAverageX = 1.0f; - NewController->IsAnalog = false; - } - - r32 Threshold = 0.5f; - Win32ProcessXInputDigitalButton( - (NewController->StickAverageX < -Threshold) ? 1 : 0, - &OldController->MoveLeft, 1, - &NewController->MoveLeft); - Win32ProcessXInputDigitalButton( - (NewController->StickAverageX > Threshold) ? 1 : 0, - &OldController->MoveRight, 1, - &NewController->MoveRight); - Win32ProcessXInputDigitalButton( - (NewController->StickAverageY < -Threshold) ? 1 : 0, - &OldController->MoveDown, 1, - &NewController->MoveDown); - Win32ProcessXInputDigitalButton( - (NewController->StickAverageY > Threshold) ? 1 : 0, - &OldController->MoveUp, 1, - &NewController->MoveUp); - - Win32ProcessXInputDigitalButton(Pad->wButtons, - &OldController->ActionDown, XINPUT_GAMEPAD_A, - &NewController->ActionDown); - Win32ProcessXInputDigitalButton(Pad->wButtons, - &OldController->ActionRight, XINPUT_GAMEPAD_B, - &NewController->ActionRight); - Win32ProcessXInputDigitalButton(Pad->wButtons, - &OldController->ActionLeft, XINPUT_GAMEPAD_X, - &NewController->ActionLeft); - Win32ProcessXInputDigitalButton(Pad->wButtons, - &OldController->ActionUp, XINPUT_GAMEPAD_Y, - &NewController->ActionUp); - Win32ProcessXInputDigitalButton(Pad->wButtons, - &OldController->LeftShoulder, XINPUT_GAMEPAD_LEFT_SHOULDER, - &NewController->LeftShoulder); - Win32ProcessXInputDigitalButton(Pad->wButtons, - &OldController->RightShoulder, XINPUT_GAMEPAD_RIGHT_SHOULDER, - &NewController->RightShoulder); - - Win32ProcessXInputDigitalButton(Pad->wButtons, - &OldController->Start, XINPUT_GAMEPAD_START, - &NewController->Start); - Win32ProcessXInputDigitalButton(Pad->wButtons, - &OldController->Back, XINPUT_GAMEPAD_BACK, - &NewController->Back); - } - else - { - // NOTE(casey): The controller is not available - NewController->IsConnected = false; - } - } - - thread_context Thread = {}; - - game_offscreen_buffer Buffer = {}; - Buffer.Memory = GlobalBackbuffer.Memory; - Buffer.Width = GlobalBackbuffer.Width; - Buffer.Height = GlobalBackbuffer.Height; - Buffer.Pitch = GlobalBackbuffer.Pitch; - Buffer.BytesPerPixel = GlobalBackbuffer.BytesPerPixel; - - if(Win32State.InputRecordingIndex) - { - Win32RecordInput(&Win32State, NewInput); - } - - if(Win32State.InputPlayingIndex) - { - Win32PlayBackInput(&Win32State, NewInput); - } - if(Game.UpdateAndRender) - { - Game.UpdateAndRender(&Thread, &GameMemory, NewInput, &Buffer); - } - - LARGE_INTEGER AudioWallClock = Win32GetWallClock(); - r32 FromBeginToAudioSeconds = Win32GetSecondsElapsed(FlipWallClock, AudioWallClock); - - DWORD PlayCursor; - DWORD WriteCursor; - if(GlobalSecondaryBuffer->GetCurrentPosition(&PlayCursor, &WriteCursor) == DS_OK) - { - /* NOTE(casey): - - Here is how sound output computation works. - - We define a safety value that is the number - of samples we think our game update loop - may vary by (let's say up to 2ms) - - When we wake up to write audio, we will look - and see what the play cursor position is and we - will forecast ahead where we think the play - cursor will be on the next frame boundary. - - We will then look to see if the write cursor is - before that by at least our safety value. If - it is, the target fill position is that frame - boundary plus one frame. This gives us perfect - audio sync in the case of a card that has low - enough latency. - - If the write cursor is _after_ that safety - margin, then we assume we can never sync the - audio perfectly, so we will write one frame's - worth of audio plus the safety margin's worth - of guard samples. - */ - if(!SoundIsValid) - { - SoundOutput.RunningSampleIndex = WriteCursor / SoundOutput.BytesPerSample; - SoundIsValid = true; - } - - DWORD ByteToLock = ((SoundOutput.RunningSampleIndex*SoundOutput.BytesPerSample) % - SoundOutput.SecondaryBufferSize); - - DWORD ExpectedSoundBytesPerFrame = - (int)((r32)(SoundOutput.SamplesPerSecond*SoundOutput.BytesPerSample) / - GameUpdateHz); - r32 SecondsLeftUntilFlip = (TargetSecondsPerFrame - FromBeginToAudioSeconds); - DWORD ExpectedBytesUntilFlip = (DWORD)((SecondsLeftUntilFlip/TargetSecondsPerFrame)*(r32)ExpectedSoundBytesPerFrame); - - DWORD ExpectedFrameBoundaryByte = PlayCursor + ExpectedBytesUntilFlip; - - DWORD SafeWriteCursor = WriteCursor; - if(SafeWriteCursor < PlayCursor) - { - SafeWriteCursor += SoundOutput.SecondaryBufferSize; - } - Assert(SafeWriteCursor >= PlayCursor); - SafeWriteCursor += SoundOutput.SafetyBytes; - - b32 AudioCardIsLowLatency = (SafeWriteCursor < ExpectedFrameBoundaryByte); - - DWORD TargetCursor = 0; - if(AudioCardIsLowLatency) - { - TargetCursor = (ExpectedFrameBoundaryByte + ExpectedSoundBytesPerFrame); - } - else - { - TargetCursor = (WriteCursor + ExpectedSoundBytesPerFrame + - SoundOutput.SafetyBytes); - } - TargetCursor = (TargetCursor % SoundOutput.SecondaryBufferSize); - - DWORD BytesToWrite = 0; - if(ByteToLock > TargetCursor) - { - BytesToWrite = (SoundOutput.SecondaryBufferSize - ByteToLock); - BytesToWrite += TargetCursor; - } - else - { - BytesToWrite = TargetCursor - ByteToLock; - } - - game_sound_output_buffer SoundBuffer = {}; - SoundBuffer.SamplesPerSecond = SoundOutput.SamplesPerSecond; - SoundBuffer.SampleCount = BytesToWrite / SoundOutput.BytesPerSample; - SoundBuffer.Samples = Samples; - if(Game.GetSoundSamples) - { - Game.GetSoundSamples(&Thread, &GameMemory, &SoundBuffer); - } - -#if HANDMADE_INTERNAL - win32_debug_time_marker *Marker = &DebugTimeMarkers[DebugTimeMarkerIndex]; - Marker->OutputPlayCursor = PlayCursor; - Marker->OutputWriteCursor = WriteCursor; - Marker->OutputLocation = ByteToLock; - Marker->OutputByteCount = BytesToWrite; - Marker->ExpectedFlipPlayCursor = ExpectedFrameBoundaryByte; - - DWORD UnwrappedWriteCursor = WriteCursor; - if(UnwrappedWriteCursor < PlayCursor) - { - UnwrappedWriteCursor += SoundOutput.SecondaryBufferSize; - } - AudioLatencyBytes = UnwrappedWriteCursor - PlayCursor; - AudioLatencySeconds = - (((r32)AudioLatencyBytes / (r32)SoundOutput.BytesPerSample) / - (r32)SoundOutput.SamplesPerSecond); - -#if 0 - char TextBuffer[256]; - _snprintf_s(TextBuffer, sizeof(TextBuffer), - "BTL:%u TC:%u BTW:%u - PC:%u WC:%u DELTA:%u (%fs)\n", - ByteToLock, TargetCursor, BytesToWrite, - PlayCursor, WriteCursor, AudioLatencyBytes, AudioLatencySeconds); - OutputDebugStringA(TextBuffer); -#endif -#endif - Win32FillSoundBuffer(&SoundOutput, ByteToLock, BytesToWrite, &SoundBuffer); - } - else - { - SoundIsValid = false; - } - - LARGE_INTEGER WorkCounter = Win32GetWallClock(); - r32 WorkSecondsElapsed = Win32GetSecondsElapsed(LastCounter, WorkCounter); - - // TODO(casey): NOT TESTED YET! PROBABLY BUGGY!!!!! - r32 SecondsElapsedForFrame = WorkSecondsElapsed; - if(SecondsElapsedForFrame < TargetSecondsPerFrame) - { - if(SleepIsGranular) - { - DWORD SleepMS = (DWORD)(1000.0f * (TargetSecondsPerFrame - - SecondsElapsedForFrame)); - if(SleepMS > 0) - { - Sleep(SleepMS); - } - } - - r32 TestSecondsElapsedForFrame = Win32GetSecondsElapsed(LastCounter, - Win32GetWallClock()); - if(TestSecondsElapsedForFrame < TargetSecondsPerFrame) - { - // TODO(casey): LOG MISSED SLEEP HERE - } - - while(SecondsElapsedForFrame < TargetSecondsPerFrame) - { - SecondsElapsedForFrame = Win32GetSecondsElapsed(LastCounter, - Win32GetWallClock()); - } - } - else - { - // TODO(casey): MISSED FRAME RATE! - // TODO(casey): Logging - } - - LARGE_INTEGER EndCounter = Win32GetWallClock(); - r32 MSPerFrame = 1000.0f*Win32GetSecondsElapsed(LastCounter, EndCounter); - LastCounter = EndCounter; - - win32_window_dimension Dimension = Win32GetWindowDimension(Window); - HDC DeviceContext = GetDC(Window); - Win32DisplayBufferInWindow(&GlobalBackbuffer, DeviceContext, - Dimension.Width, Dimension.Height); - ReleaseDC(Window, DeviceContext); - - FlipWallClock = Win32GetWallClock(); -#if HANDMADE_INTERNAL - // NOTE(casey): This is debug code - { - DWORD PlayCursor; - DWORD WriteCursor; - if(GlobalSecondaryBuffer->GetCurrentPosition(&PlayCursor, &WriteCursor) == DS_OK) - { - Assert(DebugTimeMarkerIndex < ArrayCount(DebugTimeMarkers)); - win32_debug_time_marker *Marker = &DebugTimeMarkers[DebugTimeMarkerIndex]; - Marker->FlipPlayCursor = PlayCursor; - Marker->FlipWriteCursor = WriteCursor; - } - - } -#endif - - game_input *Temp = NewInput; - NewInput = OldInput; - OldInput = Temp; - // TODO(casey): Should I clear these here? - -#if 1 - u64 EndCycleCount = __rdtsc(); - u64 CyclesElapsed = EndCycleCount - LastCycleCount; - LastCycleCount = EndCycleCount; - - r64 FPS = 0.0f; - r64 MCPF = ((r64)CyclesElapsed / (1000.0f * 1000.0f)); - - char FPSBuffer[256]; - _snprintf_s(FPSBuffer, sizeof(FPSBuffer), - "%.02fms/f, %.02ff/s, %.02fmc/f\n", MSPerFrame, FPS, MCPF); - OutputDebugStringA(FPSBuffer); -#endif - -#if HANDMADE_INTERNAL - ++DebugTimeMarkerIndex; - if(DebugTimeMarkerIndex == ArrayCount(DebugTimeMarkers)) - { - DebugTimeMarkerIndex = 0; - } -#endif - } - } - } - else - { - // TODO(casey): Logging - } - } - else - { - // TODO(casey): Logging - } - } - else - { - // TODO(casey): Logging - } - - return(0); -} -- cgit v1.2.3-70-g09d2