diff --git a/src/app/engine/sacn/sacn.h b/src/app/engine/sacn/sacn.h index 766c11e..a12c4b8 100644 --- a/src/app/engine/sacn/sacn.h +++ b/src/app/engine/sacn/sacn.h @@ -292,7 +292,7 @@ InitStreamHeader (u8* Buffer, s32 BufferSize, // internal streaming_acn -InitializeSACN (context Context) +SACN_Initialize (context Context) { streaming_acn SACN = {}; @@ -304,13 +304,13 @@ InitializeSACN (context Context) } internal void -SACNCleanup(streaming_acn* SACN, context Context) +SACN_Cleanup(streaming_acn* SACN, context Context) { Context.PlatformCloseSocket(SACN->SendSocket); } internal void -SACNUpdateSequence (streaming_acn* SACN) +SACN_UpdateSequence (streaming_acn* SACN) { // Never use 0 after the first one if (++SACN->SequenceIterator == 0) @@ -320,7 +320,7 @@ SACNUpdateSequence (streaming_acn* SACN) } internal void -SACNPrepareBufferHeader (s32 Universe, u8* Buffer, s32 BufferSize, s32 SizeReservedForHeader, streaming_acn SACN) +SACN_PrepareBufferHeader (s32 Universe, u8* Buffer, s32 BufferSize, s32 SizeReservedForHeader, streaming_acn SACN) { Assert(SizeReservedForHeader == STREAM_HEADER_SIZE); Assert(Buffer && BufferSize > 0); @@ -331,7 +331,7 @@ SACNPrepareBufferHeader (s32 Universe, u8* Buffer, s32 BufferSize, s32 SizeReser } internal u32 -SACNGetUniverseSendAddress(s32 Universe) +SACN_GetUniverseSendAddress(s32 Universe) { u8 MulticastAddressBuffer[4] = {}; MulticastAddressBuffer[0] = 239; @@ -362,7 +362,7 @@ SACN_FillBufferWithLeds(u8* BufferStart, u32 BufferSize, v2_strip Strip, led_buf internal void SACN_BuildOutputData(streaming_acn* SACN, addressed_data_buffer_list* Output, assembly_array Assemblies, led_system* LedSystem, gs_memory_arena* OutputStorage) { - SACNUpdateSequence(SACN); + SACN_UpdateSequence(SACN); // TODO(pjs): 512 is a magic number - make it a constant? s32 BufferHeaderSize = STREAM_HEADER_SIZE; @@ -378,13 +378,13 @@ SACN_BuildOutputData(streaming_acn* SACN, addressed_data_buffer_list* Output, as { v2_strip StripAt = Assembly.Strips[StripIdx]; - u32 V4SendAddress = SACNGetUniverseSendAddress(StripAt.StartUniverse); + u32 V4SendAddress = SACN_GetUniverseSendAddress(StripAt.StartUniverse); u32 SendPort = DEFAULT_STREAMING_ACN_PORT; addressed_data_buffer* Data = AddressedDataBufferList_Push(Output, BufferSize, OutputStorage); AddressedDataBuffer_SetNetworkAddress(Data, V4SendAddress, SendPort); - SACNPrepareBufferHeader(StripAt.StartUniverse, Data->Memory, Data->MemorySize, BufferHeaderSize, *SACN); + SACN_PrepareBufferHeader(StripAt.StartUniverse, Data->Memory, Data->MemorySize, BufferHeaderSize, *SACN); SACN_FillBufferWithLeds(Data->Memory + BufferHeaderSize, BufferBodySize, StripAt, *LedBuffer); } } diff --git a/src/app/foldhaus_app.cpp b/src/app/foldhaus_app.cpp index b714b7a..8914b3c 100644 --- a/src/app/foldhaus_app.cpp +++ b/src/app/foldhaus_app.cpp @@ -111,7 +111,7 @@ INITIALIZE_APPLICATION(InitializeApplication) State->Interface.Style.Margin = v2{5, 5}; State->Interface.Style.RowHeight = ui_GetTextLineHeight(State->Interface); - State->SACN = InitializeSACN(Context); + State->SACN = SACN_Initialize(Context); State->Camera.FieldOfView = 45.0f; State->Camera.AspectRatio = RectAspectRatio(State->WindowBounds); @@ -341,37 +341,39 @@ UPDATE_AND_RENDER(UpdateAndRender) } } - addressed_data_buffer_list OutputData = {0}; - switch (State->NetworkProtocol) { - case NetworkProtocol_SACN: + // NOTE(pjs): Building data buffers to be sent out to the sculpture + + addressed_data_buffer_list OutputData = {0}; + switch (State->NetworkProtocol) { - SACN_BuildOutputData(&State->SACN, &OutputData, State->Assemblies, &State->LedSystem, State->Transient); - }break; + case NetworkProtocol_SACN: + { + SACN_BuildOutputData(&State->SACN, &OutputData, State->Assemblies, &State->LedSystem, State->Transient); + }break; + + case NetworkProtocol_UART: + { + //UART_BuildOutputData(&OutputData, State, State->Transient); + }break; + + case NetworkProtocol_ArtNet: + InvalidDefaultCase; + } - case NetworkProtocol_UART: + // NOTE(pjs): Executing the job to actually send the data + if (0) { - //UART_BuildOutputData(&OutputData, State, State->Transient); - }break; - - case NetworkProtocol_ArtNet: - InvalidDefaultCase; + // TODO(pjs): This should happen on another thread + AddressedDataBufferList_SendAll(OutputData, State->SACN.SendSocket, *Context); + + /* + Saved this lien as an example of pushing onto a queue + Context->GeneralWorkQueue->PushWorkOnQueue(Context->GeneralWorkQueue, SACNSendDMXBufferListJob, Job, "SACN Send Data Job"); + */ + } } - if (0) - { - // TODO(pjs): This should happen on another thread - AddressedDataBufferList_SendAll(OutputData, State->SACN.SendSocket, *Context); - - /* -Saved this lien as an example of pushing onto a queue - Context->GeneralWorkQueue->PushWorkOnQueue(Context->GeneralWorkQueue, SACNSendDMXBufferListJob, Job, "SACN Send Data Job"); -*/ - } - - // Skipped for performance at the moment - - PushRenderOrthographic(RenderBuffer, State->WindowBounds); PushRenderClearScreen(RenderBuffer); @@ -412,7 +414,7 @@ Saved this lien as an example of pushing onto a queue CLEANUP_APPLICATION(CleanupApplication) { app_state* State = (app_state*)Context.MemoryBase; - SACNCleanup(&State->SACN, Context); + SACN_Cleanup(&State->SACN, Context); } #define FOLDHAUS_APP_CPP diff --git a/src/app/platform_win32/win32_foldhaus.cpp b/src/app/platform_win32/win32_foldhaus.cpp index b835199..338f957 100644 --- a/src/app/platform_win32/win32_foldhaus.cpp +++ b/src/app/platform_win32/win32_foldhaus.cpp @@ -15,10 +15,13 @@ #include "../foldhaus_platform.h" #include "../../gs_libs/gs_win32.cpp" +#include "win32_foldhaus_utils.h" #include "win32_foldhaus_memory.h" +#include "win32_foldhaus_fileio.h" #include "win32_foldhaus_dll.h" #include "win32_foldhaus_timing.h" -#include "win32_serial.h" +#include "win32_foldhaus_work_queue.h" +#include "win32_foldhaus_serial.h" #include "../foldhaus_renderer.cpp" @@ -31,469 +34,6 @@ char DLLLockFileName[] = "lock.tmp"; window MainWindow; -// Utils - -internal gs_string -Win32DumpErrorAndPrepareMessageBoxString(gs_memory_arena* Arena, char* Format, ...) -{ - s32 Error = GetLastError(); - gs_string ErrorDump = PushString(Arena, 4096); - PrintF(&ErrorDump, - R"FOO(Win32 Error: %s\n -Error Code: %d\n - )FOO", - __FUNCTION__, - Error); - - va_list Args; - va_start(Args, Format); - PrintFArgsList(&ErrorDump, Format, Args); - va_end(Args); - - gs_data ErrorDumpData = StringToData(ErrorDump); - - HANDLE FileHandle = CreateFileA("./crashdump.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (FileHandle != INVALID_HANDLE_VALUE) - { - DWORD BytesWritten = 0; - if (WriteFile(FileHandle, ErrorDumpData.Memory, ErrorDumpData.Size, &BytesWritten, NULL)) - { - - } - CloseHandle(FileHandle); - } - - AppendPrintF(&ErrorDump, "Program will attempt to continue. See crashdump.txt for info"); - NullTerminate(&ErrorDump); - - return ErrorDump; -} - -DEBUG_PRINT(Win32DebugPrint) -{ - Assert(IsNullTerminated(Message)); - OutputDebugStringA(Message.Str); -} - -#define PrintLastError() PrintLastError_(__FILE__, __LINE__) -internal void -PrintLastError_(char* File, u32 Line) -{ - char DebugStringData[256]; - gs_string DebugString = MakeString(DebugStringData, 0, 256); - u32 Error = GetLastError(); - PrintF(&DebugString, "%s Line %d: Win32 Error %d\n\0", File, Line, Error); - OutputDebugStringA(DebugString.Str); -} - - -internal HINSTANCE -GetHInstance() -{ - HINSTANCE Result = GetModuleHandle(NULL); - if (Result == NULL) - { - PrintLastError(); - } - return Result; -} - -/////////////////////// -// -// Fie I/O - -internal u64 -Win32HighLowToU64(u32 LowPart, u32 HighPart) -{ - ULARGE_INTEGER Time = {}; - Time.LowPart = LowPart; - Time.HighPart = HighPart; - u64 Result = Time.QuadPart; - return Result; -} - -internal u64 -Win32FileTimeToU64(FILETIME FileTime) -{ - u64 Result = Win32HighLowToU64(FileTime.dwLowDateTime, FileTime.dwHighDateTime); - return Result; -} - -GET_FILE_INFO(Win32GetFileInfo) -{ - Assert(IsNullTerminated(Path)); - gs_file_info Result = {}; - HANDLE FileHandle = CreateFileA(Path.Str, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (FileHandle != INVALID_HANDLE_VALUE) - { - Result.Path = Path; - Result.FileSize = (u64)GetFileSize(FileHandle, NULL) + 1; - FILETIME CreationTime, LastWriteTime; - if (GetFileTime(FileHandle, &CreationTime, (LPFILETIME)0, &LastWriteTime)) - { - Result.CreationTime = Win32FileTimeToU64(CreationTime); - Result.LastWriteTime = Win32FileTimeToU64(LastWriteTime); - } - else - { - PrintLastError(); - } - CloseHandle(FileHandle); - } - return Result; -} - -READ_ENTIRE_FILE(Win32ReadEntireFile) -{ - Assert(DataIsNonEmpty(Memory)); - Assert(IsNullTerminated(Path)); - - gs_file Result = {0}; - u32 Error = 0; - Result.FileInfo.Path = Path; - - HANDLE FileHandle = CreateFileA(Path.Str, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (FileHandle != INVALID_HANDLE_VALUE) - { - DWORD BytesRead = 0; - if (ReadFile(FileHandle, (LPVOID)Memory.Memory, Memory.Size - 1, (LPDWORD)(&BytesRead), NULL)) - { - Memory.Memory[Memory.Size - 1] = 0; - Result.Data = Memory; - - gs_string AbsolutePath = PushString(FileHandler.Transient, 512); - AbsolutePath.Length = GetFullPathNameA(Path.Str, AbsolutePath.Size, AbsolutePath.Str, NULL); - if (AbsolutePath.Length == 0) - { - Error = GetLastError(); - InvalidCodePath; - } - Result.FileInfo.AbsolutePath = AbsolutePath.ConstString; - } - else - { - // NOTE(Peter): If we get to this error case, it means that the file exists, - // and was successfully opened, but we can't read from it. I'm choosing to - // treat this as a legitimate error at this point. - gs_string Message = Win32DumpErrorAndPrepareMessageBoxString(FileHandler.Transient, "Attempting to read file: %S", Path); - if (MessageBox(NULL, Message.Str, "Error", MB_OK) == IDOK) - { - PostQuitMessage(-1); - } - } - CloseHandle(FileHandle); - } - else - { - - } - - return Result; -} - -WRITE_ENTIRE_FILE(Win32WriteEntireFile) -{ - Assert(DataIsNonEmpty(Data)); - Assert(IsNullTerminated(Path)); - - bool Success = false; - HANDLE FileHandle = CreateFileA(Path.Str, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (FileHandle != INVALID_HANDLE_VALUE) - { - DWORD BytesWritten = 0; - if (WriteFile(FileHandle, Data.Memory, Data.Size, &BytesWritten, NULL)) - { - Success = (BytesWritten == Data.Size); - } - else - { - gs_string Message = Win32DumpErrorAndPrepareMessageBoxString(FileHandler.Transient, "Attempting to write to file: %S", Path); - MessageBox(NULL, Message.Str, "Error", MB_OK); - } - CloseHandle(FileHandle); - } - else - { - - } - - return Success; -} - -internal FILETIME -GetFileLastWriteTime(char* Path) -{ - FILETIME Result = {}; - - WIN32_FIND_DATA FindData = {}; - HANDLE FileHandle = FindFirstFileA(Path, &FindData); - - if (FileHandle != INVALID_HANDLE_VALUE) - { - Result = FindData.ftLastWriteTime; - FindClose(FileHandle); - } - else - { - // TODO(Peter): :ErrorLogging - } - - return Result; -} - -struct temp_file_list_entry -{ - gs_file_info Info; - temp_file_list_entry* Next; -}; - -struct temp_file_list -{ - temp_file_list_entry* First; - temp_file_list_entry* Last; -}; - -internal void -Win32SetFileInfoFromFindFileData(gs_file_info* Info, WIN32_FIND_DATA FindFileData, gs_const_string SearchPath, gs_memory_arena* Storage) -{ - u32 FileNameLength = CharArrayLength(FindFileData.cFileName); - - // NOTE(Peter): String Storage - // Storing the string in the final storage means we don't have to copy the string later, and all - // strings will be continguous in memory at the calling site, though they will be before the array - gs_string FileName = PushString(Storage, SearchPath.Length + FileNameLength + 1); - PrintF(&FileName, "%S%.*s", SearchPath, FileName.Size, FindFileData.cFileName); - NullTerminate(&FileName); - - Info->FileSize = Win32HighLowToU64(FindFileData.nFileSizeLow, FindFileData.nFileSizeHigh); - Info->CreationTime = Win32FileTimeToU64(FindFileData.ftCreationTime); - Info->LastWriteTime = Win32FileTimeToU64(FindFileData.ftLastWriteTime); - Info->Path = FileName.ConstString; - Info->IsDirectory = HasFlag(FindFileData.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY); -} - -internal u32 -Win32EnumerateDirectoryIntoTempList(gs_file_handler FileHandler, temp_file_list* TempList, gs_const_string Path, gs_memory_arena* Storage, b32 Flags) -{ - u32 FilesCount = 0; - - u32 IndexOfLastSlash = FindLastFromSet(Path, "\\/"); - gs_const_string SearchPath = Substring(Path, 0, IndexOfLastSlash + 1); - - WIN32_FIND_DATA FindFileData; - HANDLE SearchHandle = FindFirstFile(Path.Str, &FindFileData); - if (SearchHandle != INVALID_HANDLE_VALUE) - { - do - { - b32 AddFile = true; - - if (HasFlag(FindFileData.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY)) - { - if (HasFlag(Flags, EnumerateDirectory_Recurse)) - { - gs_const_string SubDirName = ConstString(FindFileData.cFileName); - if (!StringsEqual(SubDirName, ConstString(".")) && - !StringsEqual(SubDirName, ConstString(".."))) - { - gs_string SubDirectoryPath = PushString(FileHandler.Transient, SearchPath.Length + SubDirName.Length + 3); - PrintF(&SubDirectoryPath, "%S%S/*\0", SearchPath, SubDirName); - FilesCount += Win32EnumerateDirectoryIntoTempList(FileHandler, TempList, SubDirectoryPath.ConstString, Storage, Flags); - } - } - - AddFile = HasFlag(Flags, EnumerateDirectory_IncludeDirectories); - } - - if (AddFile) - { - temp_file_list_entry* File = PushStruct(FileHandler.Transient, temp_file_list_entry); - *File = {0}; - Win32SetFileInfoFromFindFileData(&File->Info, FindFileData, SearchPath, Storage); - SLLPushOrInit(TempList->First, TempList->Last, File); - FilesCount += 1; - } - }while(FindNextFile(SearchHandle, &FindFileData)); - } - else - { - PrintLastError(); - } - - return FilesCount; -} - -ENUMERATE_DIRECTORY(Win32EnumerateDirectory) -{ - Assert(IsNullTerminated(Path)); - gs_file_info_array Result = {}; - - temp_file_list TempList = {}; - Result.MaxCount = Win32EnumerateDirectoryIntoTempList(FileHandler, &TempList, Path, Storage, Flags); - - Result.Values = PushArray(Storage, gs_file_info, Result.MaxCount); - for (temp_file_list_entry* FileAt = TempList.First; - FileAt != 0; - FileAt = FileAt->Next) - { - // NOTE(Peter): We don't copy the file name here because its already in Storage. - // See String Storage note above ^^ - Result.Values[Result.Count++] = FileAt->Info; - } - - return Result; -} - -/////////////////////// -// -// Job System - -struct worker_thread_entry -{ - b32 IsValid; - u32 Index; -}; - -struct worker_thread_info -{ - gs_thread_context ThreadContext; - HANDLE Handle; - gs_work_queue* Queue; -}; - -internal s32 -Win32GetThreadId() -{ - s32 Result = GetCurrentThreadId(); - return Result; -} - -internal gs_thread_context -Win32CreateThreadContext(gs_memory_arena* Transient = 0) -{ - gs_thread_context Result = {0}; - Result.ThreadInfo.ThreadID = Win32GetThreadId(); - Result.Allocator = CreateAllocator(Win32Alloc, Win32Free); - if (Transient != 0) - { - Result.Transient = Transient; - } - else - { - Result.Transient = (gs_memory_arena*)AllocatorAlloc(Result.Allocator, sizeof(gs_memory_arena)).Memory; - *Result.Transient = CreateMemoryArena(Result.Allocator); - } - Result.FileHandler = CreateFileHandler(Win32GetFileInfo, - Win32ReadEntireFile, - Win32WriteEntireFile, - Win32EnumerateDirectory, - Result.Transient); - - return Result; -} - -PUSH_WORK_ON_QUEUE(Win32PushWorkOnQueue) -{ -#ifdef DEBUG - // NOTE(Peter): Just prints out the names of all the pending jobs if we end up - // overflowing the buffer - if (Queue->JobsCount >= Queue->JobsMax) - { - gs_string DebugString = MakeString((char*)malloc(256), 256); - for (u32 i = 0; i < Queue->JobsCount; i++) - { - PrintF(&DebugString, "%d %s\n", i, Queue->Jobs[i].JobName); - NullTerminate(&DebugString); - OutputDebugStringA(DebugString.Str); - } - } -#endif - Assert(Queue->JobsCount < Queue->JobsMax); - - gs_threaded_job* Job = Queue->Jobs + Queue->JobsCount; - Job->WorkProc = WorkProc; - Job->Data = Data; -#ifdef DEBUG - Job->JobName = JobName; -#endif - - // Complete Past Writes before Future Writes - _WriteBarrier(); - _mm_sfence(); - - ++Queue->JobsCount; - ReleaseSemaphore(Queue->SemaphoreHandle, 1, 0); -} - -internal worker_thread_entry -CompleteAndTakeNextJob(gs_work_queue* Queue, worker_thread_entry Completed, gs_thread_context Context) -{ - if (Completed.IsValid) - { - InterlockedIncrement((LONG volatile*)&Queue->JobsCompleted); - } - - worker_thread_entry Result = {}; - Result.IsValid = false; - - u32 OriginalNextJobIndex = Queue->NextJobIndex; - while (OriginalNextJobIndex < Queue->JobsCount) - { - u32 Index = InterlockedCompareExchange((LONG volatile*)&Queue->NextJobIndex, - OriginalNextJobIndex + 1, - OriginalNextJobIndex); - if (Index == OriginalNextJobIndex) - { - Result.Index = Index; - Result.IsValid = true; - break; - } - OriginalNextJobIndex = Queue->NextJobIndex; - } - - return Result; -} - -COMPLETE_QUEUE_WORK(Win32DoQueueWorkUntilDone) -{ - worker_thread_entry Entry = {}; - Entry.IsValid = false; - while (Queue->JobsCompleted < Queue->JobsCount) - { - Entry = CompleteAndTakeNextJob(Queue, Entry, Context); - if (Entry.IsValid) - { - Queue->Jobs[Entry.Index].WorkProc(Context, Queue->Jobs[Entry.Index].Data); - } - } -} - -DWORD WINAPI -WorkerThreadProc (LPVOID InputThreadInfo) -{ - worker_thread_info* ThreadInfo = (worker_thread_info*)InputThreadInfo; - ThreadInfo->ThreadContext = Win32CreateThreadContext(); - - worker_thread_entry Entry = {}; - Entry.IsValid = false; - while (true) - { - ClearArena(ThreadInfo->ThreadContext.Transient); - Entry = CompleteAndTakeNextJob(ThreadInfo->Queue, Entry, ThreadInfo->ThreadContext); - if (Entry.IsValid) - { - ThreadInfo->Queue->Jobs[Entry.Index].WorkProc(ThreadInfo->ThreadContext, - ThreadInfo->Queue->Jobs[Entry.Index].Data); - } - else - { - WaitForSingleObjectEx(ThreadInfo->Queue->SemaphoreHandle, INFINITE, 0); - } - } - - return 0; -} - PLATFORM_GET_GPU_TEXTURE_HANDLE(Win32GetGPUTextureHandle) { s32 Handle = SubmitTexture(Memory, Width, Height); diff --git a/src/app/platform_win32/win32_foldhaus_fileio.h b/src/app/platform_win32/win32_foldhaus_fileio.h index a45c25e..2c4059c 100644 --- a/src/app/platform_win32/win32_foldhaus_fileio.h +++ b/src/app/platform_win32/win32_foldhaus_fileio.h @@ -8,5 +8,247 @@ // #ifndef WIN32_FOLDHAUS_FILEIO_H +internal u64 +Win32HighLowToU64(u32 LowPart, u32 HighPart) +{ + ULARGE_INTEGER Time = {}; + Time.LowPart = LowPart; + Time.HighPart = HighPart; + u64 Result = Time.QuadPart; + return Result; +} + +internal u64 +Win32FileTimeToU64(FILETIME FileTime) +{ + u64 Result = Win32HighLowToU64(FileTime.dwLowDateTime, FileTime.dwHighDateTime); + return Result; +} + +GET_FILE_INFO(Win32GetFileInfo) +{ + Assert(IsNullTerminated(Path)); + gs_file_info Result = {}; + HANDLE FileHandle = CreateFileA(Path.Str, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (FileHandle != INVALID_HANDLE_VALUE) + { + Result.Path = Path; + Result.FileSize = (u64)GetFileSize(FileHandle, NULL) + 1; + FILETIME CreationTime, LastWriteTime; + if (GetFileTime(FileHandle, &CreationTime, (LPFILETIME)0, &LastWriteTime)) + { + Result.CreationTime = Win32FileTimeToU64(CreationTime); + Result.LastWriteTime = Win32FileTimeToU64(LastWriteTime); + } + else + { + PrintLastError(); + } + CloseHandle(FileHandle); + } + return Result; +} + +READ_ENTIRE_FILE(Win32ReadEntireFile) +{ + Assert(DataIsNonEmpty(Memory)); + Assert(IsNullTerminated(Path)); + + gs_file Result = {0}; + u32 Error = 0; + Result.FileInfo.Path = Path; + + HANDLE FileHandle = CreateFileA(Path.Str, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (FileHandle != INVALID_HANDLE_VALUE) + { + DWORD BytesRead = 0; + if (ReadFile(FileHandle, (LPVOID)Memory.Memory, Memory.Size - 1, (LPDWORD)(&BytesRead), NULL)) + { + Memory.Memory[Memory.Size - 1] = 0; + Result.Data = Memory; + + gs_string AbsolutePath = PushString(FileHandler.Transient, 512); + AbsolutePath.Length = GetFullPathNameA(Path.Str, AbsolutePath.Size, AbsolutePath.Str, NULL); + if (AbsolutePath.Length == 0) + { + Error = GetLastError(); + InvalidCodePath; + } + Result.FileInfo.AbsolutePath = AbsolutePath.ConstString; + } + else + { + // NOTE(Peter): If we get to this error case, it means that the file exists, + // and was successfully opened, but we can't read from it. I'm choosing to + // treat this as a legitimate error at this point. + gs_string Message = Win32DumpErrorAndPrepareMessageBoxString(FileHandler.Transient, "Attempting to read file: %S", Path); + if (MessageBox(NULL, Message.Str, "Error", MB_OK) == IDOK) + { + PostQuitMessage(-1); + } + } + CloseHandle(FileHandle); + } + else + { + + } + + return Result; +} + +WRITE_ENTIRE_FILE(Win32WriteEntireFile) +{ + Assert(DataIsNonEmpty(Data)); + Assert(IsNullTerminated(Path)); + + bool Success = false; + HANDLE FileHandle = CreateFileA(Path.Str, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (FileHandle != INVALID_HANDLE_VALUE) + { + DWORD BytesWritten = 0; + if (WriteFile(FileHandle, Data.Memory, Data.Size, &BytesWritten, NULL)) + { + Success = (BytesWritten == Data.Size); + } + else + { + gs_string Message = Win32DumpErrorAndPrepareMessageBoxString(FileHandler.Transient, "Attempting to write to file: %S", Path); + MessageBox(NULL, Message.Str, "Error", MB_OK); + } + CloseHandle(FileHandle); + } + else + { + + } + + return Success; +} + +internal FILETIME +GetFileLastWriteTime(char* Path) +{ + FILETIME Result = {}; + + WIN32_FIND_DATA FindData = {}; + HANDLE FileHandle = FindFirstFileA(Path, &FindData); + + if (FileHandle != INVALID_HANDLE_VALUE) + { + Result = FindData.ftLastWriteTime; + FindClose(FileHandle); + } + else + { + // TODO(Peter): :ErrorLogging + } + + return Result; +} + +struct temp_file_list_entry +{ + gs_file_info Info; + temp_file_list_entry* Next; +}; + +struct temp_file_list +{ + temp_file_list_entry* First; + temp_file_list_entry* Last; +}; + +internal void +Win32SetFileInfoFromFindFileData(gs_file_info* Info, WIN32_FIND_DATA FindFileData, gs_const_string SearchPath, gs_memory_arena* Storage) +{ + u32 FileNameLength = CharArrayLength(FindFileData.cFileName); + + // NOTE(Peter): String Storage + // Storing the string in the final storage means we don't have to copy the string later, and all + // strings will be continguous in memory at the calling site, though they will be before the array + gs_string FileName = PushString(Storage, SearchPath.Length + FileNameLength + 1); + PrintF(&FileName, "%S%.*s", SearchPath, FileName.Size, FindFileData.cFileName); + NullTerminate(&FileName); + + Info->FileSize = Win32HighLowToU64(FindFileData.nFileSizeLow, FindFileData.nFileSizeHigh); + Info->CreationTime = Win32FileTimeToU64(FindFileData.ftCreationTime); + Info->LastWriteTime = Win32FileTimeToU64(FindFileData.ftLastWriteTime); + Info->Path = FileName.ConstString; + Info->IsDirectory = HasFlag(FindFileData.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY); +} + +internal u32 +Win32EnumerateDirectoryIntoTempList(gs_file_handler FileHandler, temp_file_list* TempList, gs_const_string Path, gs_memory_arena* Storage, b32 Flags) +{ + u32 FilesCount = 0; + + u32 IndexOfLastSlash = FindLastFromSet(Path, "\\/"); + gs_const_string SearchPath = Substring(Path, 0, IndexOfLastSlash + 1); + + WIN32_FIND_DATA FindFileData; + HANDLE SearchHandle = FindFirstFile(Path.Str, &FindFileData); + if (SearchHandle != INVALID_HANDLE_VALUE) + { + do + { + b32 AddFile = true; + + if (HasFlag(FindFileData.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY)) + { + if (HasFlag(Flags, EnumerateDirectory_Recurse)) + { + gs_const_string SubDirName = ConstString(FindFileData.cFileName); + if (!StringsEqual(SubDirName, ConstString(".")) && + !StringsEqual(SubDirName, ConstString(".."))) + { + gs_string SubDirectoryPath = PushString(FileHandler.Transient, SearchPath.Length + SubDirName.Length + 3); + PrintF(&SubDirectoryPath, "%S%S/*\0", SearchPath, SubDirName); + FilesCount += Win32EnumerateDirectoryIntoTempList(FileHandler, TempList, SubDirectoryPath.ConstString, Storage, Flags); + } + } + + AddFile = HasFlag(Flags, EnumerateDirectory_IncludeDirectories); + } + + if (AddFile) + { + temp_file_list_entry* File = PushStruct(FileHandler.Transient, temp_file_list_entry); + *File = {0}; + Win32SetFileInfoFromFindFileData(&File->Info, FindFileData, SearchPath, Storage); + SLLPushOrInit(TempList->First, TempList->Last, File); + FilesCount += 1; + } + }while(FindNextFile(SearchHandle, &FindFileData)); + } + else + { + PrintLastError(); + } + + return FilesCount; +} + +ENUMERATE_DIRECTORY(Win32EnumerateDirectory) +{ + Assert(IsNullTerminated(Path)); + gs_file_info_array Result = {}; + + temp_file_list TempList = {}; + Result.MaxCount = Win32EnumerateDirectoryIntoTempList(FileHandler, &TempList, Path, Storage, Flags); + + Result.Values = PushArray(Storage, gs_file_info, Result.MaxCount); + for (temp_file_list_entry* FileAt = TempList.First; + FileAt != 0; + FileAt = FileAt->Next) + { + // NOTE(Peter): We don't copy the file name here because its already in Storage. + // See String Storage note above ^^ + Result.Values[Result.Count++] = FileAt->Info; + } + + return Result; +} + #define WIN32_FOLDHAUS_FILEIO_H #endif // WIN32_FOLDHAUS_FILEIO_H \ No newline at end of file diff --git a/src/app/platform_win32/win32_serial.h b/src/app/platform_win32/win32_foldhaus_serial.h similarity index 100% rename from src/app/platform_win32/win32_serial.h rename to src/app/platform_win32/win32_foldhaus_serial.h diff --git a/src/app/platform_win32/win32_foldhaus_utils.h b/src/app/platform_win32/win32_foldhaus_utils.h new file mode 100644 index 0000000..5392c9f --- /dev/null +++ b/src/app/platform_win32/win32_foldhaus_utils.h @@ -0,0 +1,75 @@ +// +// File: win32_foldhaus_utils.h +// Author: Peter Slattery +// Creation Date: 2020-10-01 +// +#ifndef WIN32_FOLDHAUS_UTILS_H + +internal gs_string +Win32DumpErrorAndPrepareMessageBoxString(gs_memory_arena* Arena, char* Format, ...) +{ + s32 Error = GetLastError(); + gs_string ErrorDump = PushString(Arena, 4096); + PrintF(&ErrorDump, + R"FOO(Win32 Error: %s\n +Error Code: %d\n + )FOO", + __FUNCTION__, + Error); + + va_list Args; + va_start(Args, Format); + PrintFArgsList(&ErrorDump, Format, Args); + va_end(Args); + + gs_data ErrorDumpData = StringToData(ErrorDump); + + HANDLE FileHandle = CreateFileA("./crashdump.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (FileHandle != INVALID_HANDLE_VALUE) + { + DWORD BytesWritten = 0; + if (WriteFile(FileHandle, ErrorDumpData.Memory, ErrorDumpData.Size, &BytesWritten, NULL)) + { + + } + CloseHandle(FileHandle); + } + + AppendPrintF(&ErrorDump, "Program will attempt to continue. See crashdump.txt for info"); + NullTerminate(&ErrorDump); + + return ErrorDump; +} + +DEBUG_PRINT(Win32DebugPrint) +{ + Assert(IsNullTerminated(Message)); + OutputDebugStringA(Message.Str); +} + +#define PrintLastError() PrintLastError_(__FILE__, __LINE__) +internal void +PrintLastError_(char* File, u32 Line) +{ + char DebugStringData[256]; + gs_string DebugString = MakeString(DebugStringData, 0, 256); + u32 Error = GetLastError(); + PrintF(&DebugString, "%s Line %d: Win32 Error %d\n\0", File, Line, Error); + OutputDebugStringA(DebugString.Str); +} + + +internal HINSTANCE +GetHInstance() +{ + HINSTANCE Result = GetModuleHandle(NULL); + if (Result == NULL) + { + PrintLastError(); + } + return Result; +} + + +#define WIN32_FOLDHAUS_UTILS_H +#endif // WIN32_FOLDHAUS_UTILS_H \ No newline at end of file diff --git a/src/app/platform_win32/win32_foldhaus_work_queue.h b/src/app/platform_win32/win32_foldhaus_work_queue.h new file mode 100644 index 0000000..2db58f3 --- /dev/null +++ b/src/app/platform_win32/win32_foldhaus_work_queue.h @@ -0,0 +1,155 @@ +// +// File: win32_foldhaus_work_queue.h +// Author: Peter Slattery +// Creation Date: 2020-10-01 +// +#ifndef WIN32_FOLDHAUS_WORK_QUEUE_H + +struct worker_thread_entry +{ + b32 IsValid; + u32 Index; +}; + +struct worker_thread_info +{ + gs_thread_context ThreadContext; + HANDLE Handle; + gs_work_queue* Queue; +}; + +internal s32 +Win32GetThreadId() +{ + s32 Result = GetCurrentThreadId(); + return Result; +} + +internal gs_thread_context +Win32CreateThreadContext(gs_memory_arena* Transient = 0) +{ + gs_thread_context Result = {0}; + Result.ThreadInfo.ThreadID = Win32GetThreadId(); + Result.Allocator = CreateAllocator(Win32Alloc, Win32Free); + if (Transient != 0) + { + Result.Transient = Transient; + } + else + { + Result.Transient = (gs_memory_arena*)AllocatorAlloc(Result.Allocator, sizeof(gs_memory_arena)).Memory; + *Result.Transient = CreateMemoryArena(Result.Allocator); + } + Result.FileHandler = CreateFileHandler(Win32GetFileInfo, + Win32ReadEntireFile, + Win32WriteEntireFile, + Win32EnumerateDirectory, + Result.Transient); + + return Result; +} + +PUSH_WORK_ON_QUEUE(Win32PushWorkOnQueue) +{ +#ifdef DEBUG + // NOTE(Peter): Just prints out the names of all the pending jobs if we end up + // overflowing the buffer + if (Queue->JobsCount >= Queue->JobsMax) + { + gs_string DebugString = MakeString((char*)malloc(256), 256); + for (u32 i = 0; i < Queue->JobsCount; i++) + { + PrintF(&DebugString, "%d %s\n", i, Queue->Jobs[i].JobName); + NullTerminate(&DebugString); + OutputDebugStringA(DebugString.Str); + } + } +#endif + Assert(Queue->JobsCount < Queue->JobsMax); + + gs_threaded_job* Job = Queue->Jobs + Queue->JobsCount; + Job->WorkProc = WorkProc; + Job->Data = Data; +#ifdef DEBUG + Job->JobName = JobName; +#endif + + // Complete Past Writes before Future Writes + _WriteBarrier(); + _mm_sfence(); + + ++Queue->JobsCount; + ReleaseSemaphore(Queue->SemaphoreHandle, 1, 0); +} + +internal worker_thread_entry +CompleteAndTakeNextJob(gs_work_queue* Queue, worker_thread_entry Completed, gs_thread_context Context) +{ + if (Completed.IsValid) + { + InterlockedIncrement((LONG volatile*)&Queue->JobsCompleted); + } + + worker_thread_entry Result = {}; + Result.IsValid = false; + + u32 OriginalNextJobIndex = Queue->NextJobIndex; + while (OriginalNextJobIndex < Queue->JobsCount) + { + u32 Index = InterlockedCompareExchange((LONG volatile*)&Queue->NextJobIndex, + OriginalNextJobIndex + 1, + OriginalNextJobIndex); + if (Index == OriginalNextJobIndex) + { + Result.Index = Index; + Result.IsValid = true; + break; + } + OriginalNextJobIndex = Queue->NextJobIndex; + } + + return Result; +} + +COMPLETE_QUEUE_WORK(Win32DoQueueWorkUntilDone) +{ + worker_thread_entry Entry = {}; + Entry.IsValid = false; + while (Queue->JobsCompleted < Queue->JobsCount) + { + Entry = CompleteAndTakeNextJob(Queue, Entry, Context); + if (Entry.IsValid) + { + Queue->Jobs[Entry.Index].WorkProc(Context, Queue->Jobs[Entry.Index].Data); + } + } +} + +DWORD WINAPI +WorkerThreadProc (LPVOID InputThreadInfo) +{ + worker_thread_info* ThreadInfo = (worker_thread_info*)InputThreadInfo; + ThreadInfo->ThreadContext = Win32CreateThreadContext(); + + worker_thread_entry Entry = {}; + Entry.IsValid = false; + while (true) + { + ClearArena(ThreadInfo->ThreadContext.Transient); + Entry = CompleteAndTakeNextJob(ThreadInfo->Queue, Entry, ThreadInfo->ThreadContext); + if (Entry.IsValid) + { + ThreadInfo->Queue->Jobs[Entry.Index].WorkProc(ThreadInfo->ThreadContext, + ThreadInfo->Queue->Jobs[Entry.Index].Data); + } + else + { + WaitForSingleObjectEx(ThreadInfo->Queue->SemaphoreHandle, INFINITE, 0); + } + } + + return 0; +} + +#define WIN32_FOLDHAUS_WORK_QUEUE_H +#endif // WIN32_FOLDHAUS_WORK_QUEUE_H \ No newline at end of file