Naming convention updates, separated out the work queue into its own file
This commit is contained in:
parent
78d44b9348
commit
0022efea8e
|
@ -292,7 +292,7 @@ InitStreamHeader (u8* Buffer, s32 BufferSize,
|
||||||
//
|
//
|
||||||
|
|
||||||
internal streaming_acn
|
internal streaming_acn
|
||||||
InitializeSACN (context Context)
|
SACN_Initialize (context Context)
|
||||||
{
|
{
|
||||||
streaming_acn SACN = {};
|
streaming_acn SACN = {};
|
||||||
|
|
||||||
|
@ -304,13 +304,13 @@ InitializeSACN (context Context)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void
|
internal void
|
||||||
SACNCleanup(streaming_acn* SACN, context Context)
|
SACN_Cleanup(streaming_acn* SACN, context Context)
|
||||||
{
|
{
|
||||||
Context.PlatformCloseSocket(SACN->SendSocket);
|
Context.PlatformCloseSocket(SACN->SendSocket);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void
|
internal void
|
||||||
SACNUpdateSequence (streaming_acn* SACN)
|
SACN_UpdateSequence (streaming_acn* SACN)
|
||||||
{
|
{
|
||||||
// Never use 0 after the first one
|
// Never use 0 after the first one
|
||||||
if (++SACN->SequenceIterator == 0)
|
if (++SACN->SequenceIterator == 0)
|
||||||
|
@ -320,7 +320,7 @@ SACNUpdateSequence (streaming_acn* SACN)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void
|
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(SizeReservedForHeader == STREAM_HEADER_SIZE);
|
||||||
Assert(Buffer && BufferSize > 0);
|
Assert(Buffer && BufferSize > 0);
|
||||||
|
@ -331,7 +331,7 @@ SACNPrepareBufferHeader (s32 Universe, u8* Buffer, s32 BufferSize, s32 SizeReser
|
||||||
}
|
}
|
||||||
|
|
||||||
internal u32
|
internal u32
|
||||||
SACNGetUniverseSendAddress(s32 Universe)
|
SACN_GetUniverseSendAddress(s32 Universe)
|
||||||
{
|
{
|
||||||
u8 MulticastAddressBuffer[4] = {};
|
u8 MulticastAddressBuffer[4] = {};
|
||||||
MulticastAddressBuffer[0] = 239;
|
MulticastAddressBuffer[0] = 239;
|
||||||
|
@ -362,7 +362,7 @@ SACN_FillBufferWithLeds(u8* BufferStart, u32 BufferSize, v2_strip Strip, led_buf
|
||||||
internal void
|
internal void
|
||||||
SACN_BuildOutputData(streaming_acn* SACN, addressed_data_buffer_list* Output, assembly_array Assemblies, led_system* LedSystem, gs_memory_arena* OutputStorage)
|
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?
|
// TODO(pjs): 512 is a magic number - make it a constant?
|
||||||
s32 BufferHeaderSize = STREAM_HEADER_SIZE;
|
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];
|
v2_strip StripAt = Assembly.Strips[StripIdx];
|
||||||
|
|
||||||
u32 V4SendAddress = SACNGetUniverseSendAddress(StripAt.StartUniverse);
|
u32 V4SendAddress = SACN_GetUniverseSendAddress(StripAt.StartUniverse);
|
||||||
u32 SendPort = DEFAULT_STREAMING_ACN_PORT;
|
u32 SendPort = DEFAULT_STREAMING_ACN_PORT;
|
||||||
|
|
||||||
addressed_data_buffer* Data = AddressedDataBufferList_Push(Output, BufferSize, OutputStorage);
|
addressed_data_buffer* Data = AddressedDataBufferList_Push(Output, BufferSize, OutputStorage);
|
||||||
AddressedDataBuffer_SetNetworkAddress(Data, V4SendAddress, SendPort);
|
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);
|
SACN_FillBufferWithLeds(Data->Memory + BufferHeaderSize, BufferBodySize, StripAt, *LedBuffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,7 +111,7 @@ INITIALIZE_APPLICATION(InitializeApplication)
|
||||||
State->Interface.Style.Margin = v2{5, 5};
|
State->Interface.Style.Margin = v2{5, 5};
|
||||||
State->Interface.Style.RowHeight = ui_GetTextLineHeight(State->Interface);
|
State->Interface.Style.RowHeight = ui_GetTextLineHeight(State->Interface);
|
||||||
|
|
||||||
State->SACN = InitializeSACN(Context);
|
State->SACN = SACN_Initialize(Context);
|
||||||
|
|
||||||
State->Camera.FieldOfView = 45.0f;
|
State->Camera.FieldOfView = 45.0f;
|
||||||
State->Camera.AspectRatio = RectAspectRatio(State->WindowBounds);
|
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);
|
case NetworkProtocol_SACN:
|
||||||
}break;
|
{
|
||||||
|
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);
|
// TODO(pjs): This should happen on another thread
|
||||||
}break;
|
AddressedDataBufferList_SendAll(OutputData, State->SACN.SendSocket, *Context);
|
||||||
|
|
||||||
case NetworkProtocol_ArtNet:
|
/*
|
||||||
InvalidDefaultCase;
|
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);
|
PushRenderOrthographic(RenderBuffer, State->WindowBounds);
|
||||||
PushRenderClearScreen(RenderBuffer);
|
PushRenderClearScreen(RenderBuffer);
|
||||||
|
@ -412,7 +414,7 @@ Saved this lien as an example of pushing onto a queue
|
||||||
CLEANUP_APPLICATION(CleanupApplication)
|
CLEANUP_APPLICATION(CleanupApplication)
|
||||||
{
|
{
|
||||||
app_state* State = (app_state*)Context.MemoryBase;
|
app_state* State = (app_state*)Context.MemoryBase;
|
||||||
SACNCleanup(&State->SACN, Context);
|
SACN_Cleanup(&State->SACN, Context);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define FOLDHAUS_APP_CPP
|
#define FOLDHAUS_APP_CPP
|
||||||
|
|
|
@ -15,10 +15,13 @@
|
||||||
#include "../foldhaus_platform.h"
|
#include "../foldhaus_platform.h"
|
||||||
|
|
||||||
#include "../../gs_libs/gs_win32.cpp"
|
#include "../../gs_libs/gs_win32.cpp"
|
||||||
|
#include "win32_foldhaus_utils.h"
|
||||||
#include "win32_foldhaus_memory.h"
|
#include "win32_foldhaus_memory.h"
|
||||||
|
#include "win32_foldhaus_fileio.h"
|
||||||
#include "win32_foldhaus_dll.h"
|
#include "win32_foldhaus_dll.h"
|
||||||
#include "win32_foldhaus_timing.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"
|
#include "../foldhaus_renderer.cpp"
|
||||||
|
|
||||||
|
@ -31,469 +34,6 @@ char DLLLockFileName[] = "lock.tmp";
|
||||||
|
|
||||||
window MainWindow;
|
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)
|
PLATFORM_GET_GPU_TEXTURE_HANDLE(Win32GetGPUTextureHandle)
|
||||||
{
|
{
|
||||||
s32 Handle = SubmitTexture(Memory, Width, Height);
|
s32 Handle = SubmitTexture(Memory, Width, Height);
|
||||||
|
|
|
@ -8,5 +8,247 @@
|
||||||
//
|
//
|
||||||
#ifndef WIN32_FOLDHAUS_FILEIO_H
|
#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
|
#define WIN32_FOLDHAUS_FILEIO_H
|
||||||
#endif // WIN32_FOLDHAUS_FILEIO_H
|
#endif // WIN32_FOLDHAUS_FILEIO_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
|
|
@ -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
|
Loading…
Reference in New Issue