#if !defined(HANDMADE_PLATFORM_H) /* ======================================================================== $File: $ $Date: $ $Revision: $ $Creator: Casey Muratori $ $Notice: (C) Copyright 2014 by Molly Rocket, Inc. All Rights Reserved. $ ======================================================================== */ // TODO(casey): Have the meta-parser ignore its own #define #define introspect(params) #define counted_pointer(params) #include "handmade_config.h" /* NOTE(casey): HANDMADE_INTERNAL: 0 - Build for public release 1 - Build for developer only HANDMADE_SLOW: 0 - Not slow code allowed! 1 - Slow code welcome. */ #ifdef __cplusplus extern "C" { #endif // // NOTE(casey): Compilers // #if !defined(COMPILER_MSVC) #define COMPILER_MSVC 0 #endif #if !defined(COMPILER_LLVM) #define COMPILER_LLVM 0 #endif #if !COMPILER_MSVC && !COMPILER_LLVM #if _MSC_VER #undef COMPILER_MSVC #define COMPILER_MSVC 1 #else // TODO(casey): Moar compilerz!!! #undef COMPILER_LLVM #define COMPILER_LLVM 1 #endif #endif #if COMPILER_MSVC #include #elif COMPILER_LLVM #include #else #error SEE/NEON optimizations are not available for this compiler yet!!!! #endif // // NOTE(casey): Types // #include #include #include #include typedef int8_t int8; typedef int16_t int16; typedef int32_t int32; typedef int64_t int64; typedef int32 bool32; typedef uint8_t uint8; typedef uint16_t uint16; typedef uint32_t uint32; typedef uint64_t uint64; typedef intptr_t intptr; typedef uintptr_t uintptr; typedef size_t memory_index; typedef float real32; typedef double real64; typedef int8 s8; typedef int8 s08; typedef int16 s16; typedef int32 s32; typedef int64 s64; typedef bool32 b32; typedef uint8 u8; typedef uint8 u08; typedef uint16 u16; typedef uint32 u32; typedef uint64 u64; typedef real32 r32; typedef real64 r64; #pragma pack(push, 1) struct bitmap_id { u32 Value; }; struct sound_id { u32 Value; }; struct font_id { u32 Value; }; #pragma pack(pop) union v2 { struct { real32 x, y; }; struct { real32 u, v; }; real32 E[2]; }; union v3 { struct { real32 x, y, z; }; struct { real32 u, v, w; }; struct { real32 r, g, b; }; struct { v2 xy; real32 Ignored0_; }; struct { real32 Ignored1_; v2 yz; }; struct { v2 uv; real32 Ignored2_; }; struct { real32 Ignored3_; v2 vw; }; real32 E[3]; }; union v4 { struct { union { v3 xyz; struct { real32 x, y, z; }; }; real32 w; }; struct { union { v3 rgb; struct { real32 r, g, b; }; }; real32 a; }; struct { v2 xy; real32 Ignored0_; real32 Ignored1_; }; struct { real32 Ignored2_; v2 yz; real32 Ignored3_; }; struct { real32 Ignored4_; real32 Ignored5_; v2 zw; }; real32 E[4]; }; introspect(category:"math") struct rectangle2 { v2 Min; v2 Max; }; introspect(category:"math") struct rectangle3 { v3 Min; v3 Max; }; #define Real32Maximum FLT_MAX #if !defined(internal) #define internal static #endif #define local_persist static #define global_variable static #define Pi32 3.14159265359f #define Tau32 6.28318530717958647692f #if HANDMADE_SLOW // TODO(casey): Complete assertion macro - don't worry everyone! #define Assert(Expression) if(!(Expression)) {*(int *)0 = 0;} #else #define Assert(Expression) #endif #define InvalidCodePath Assert(!"InvalidCodePath") #define InvalidDefaultCase default: {InvalidCodePath;} break #define Kilobytes(Value) ((Value)*1024LL) #define Megabytes(Value) (Kilobytes(Value)*1024LL) #define Gigabytes(Value) (Megabytes(Value)*1024LL) #define Terabytes(Value) (Gigabytes(Value)*1024LL) #define ArrayCount(Array) (sizeof(Array) / sizeof((Array)[0])) // TODO(casey): swap, min, max ... macros??? #define AlignPow2(Value, Alignment) ((Value + ((Alignment) - 1)) & ~((Alignment) - 1)) #define Align4(Value) ((Value + 3) & ~3) #define Align8(Value) ((Value + 7) & ~7) #define Align16(Value) ((Value + 15) & ~15) inline uint32 SafeTruncateUInt64(uint64 Value) { // TODO(casey): Defines for maximum values Assert(Value <= 0xFFFFFFFF); uint32 Result = (uint32)Value; return(Result); } /* NOTE(casey): Services that the platform layer provides to the game */ #if HANDMADE_INTERNAL /* IMPORTANT(casey): These are NOT for doing anything in the shipping game - they are blocking and the write doesn't protect against lost data! */ typedef struct debug_read_file_result { uint32 ContentsSize; void *Contents; } debug_read_file_result; typedef struct debug_executing_process { u64 OSHandle; } debug_executing_process; typedef struct debug_process_state { b32 StartedSuccessfully; b32 IsRunning; s32 ReturnCode; } debug_process_state; #define DEBUG_PLATFORM_FREE_FILE_MEMORY(name) void name(void *Memory) typedef DEBUG_PLATFORM_FREE_FILE_MEMORY(debug_platform_free_file_memory); #define DEBUG_PLATFORM_READ_ENTIRE_FILE(name) debug_read_file_result name(char *Filename) typedef DEBUG_PLATFORM_READ_ENTIRE_FILE(debug_platform_read_entire_file); #define DEBUG_PLATFORM_WRITE_ENTIRE_FILE(name) bool32 name(char *Filename, uint32 MemorySize, void *Memory) typedef DEBUG_PLATFORM_WRITE_ENTIRE_FILE(debug_platform_write_entire_file); #define DEBUG_PLATFORM_EXECUTE_SYSTEM_COMMAND(name) debug_executing_process name(char *Path, char *Command, char *CommandLine) typedef DEBUG_PLATFORM_EXECUTE_SYSTEM_COMMAND(debug_platform_execute_system_command); // TODO(casey): Do we want a formal release mechanism here? #define DEBUG_PLATFORM_GET_PROCESS_STATE(name) debug_process_state name(debug_executing_process Process) typedef DEBUG_PLATFORM_GET_PROCESS_STATE(debug_platform_get_process_state); // TODO(casey): Actually start using this??? extern struct game_memory *DebugGlobalMemory; #endif /* NOTE(casey): Services that the game provides to the platform layer. (this may expand in the future - sound on separate thread, etc.) */ // FOUR THINGS - timing, controller/keyboard input, bitmap buffer to use, sound buffer to use // TODO(casey): In the future, rendering _specifically_ will become a three-tiered abstraction!!! #define BITMAP_BYTES_PER_PIXEL 4 typedef struct game_offscreen_buffer { // NOTE(casey): Pixels are always 32-bits wide, Memory Order BB GG RR XX void *Memory; int Width; int Height; int Pitch; } game_offscreen_buffer; typedef struct game_sound_output_buffer { int SamplesPerSecond; int SampleCount; // IMPORTANT(casey): Samples must be padded to a multiple of 4 samples! int16 *Samples; } game_sound_output_buffer; typedef struct game_button_state { int HalfTransitionCount; bool32 EndedDown; } game_button_state; typedef struct game_controller_input { bool32 IsConnected; bool32 IsAnalog; real32 StickAverageX; real32 StickAverageY; union { game_button_state Buttons[12]; struct { game_button_state MoveUp; game_button_state MoveDown; game_button_state MoveLeft; game_button_state MoveRight; game_button_state ActionUp; game_button_state ActionDown; game_button_state ActionLeft; game_button_state ActionRight; game_button_state LeftShoulder; game_button_state RightShoulder; game_button_state Back; game_button_state Start; // NOTE(casey): All buttons must be added above this line game_button_state Terminator; }; }; } game_controller_input; enum game_input_mouse_button { PlatformMouseButton_Left, PlatformMouseButton_Middle, PlatformMouseButton_Right, PlatformMouseButton_Extended0, PlatformMouseButton_Extended1, PlatformMouseButton_Count, }; typedef struct game_input { r32 dtForFrame; game_controller_input Controllers[5]; // NOTE(casey): For debugging only game_button_state MouseButtons[PlatformMouseButton_Count]; r32 MouseX, MouseY, MouseZ; b32 ShiftDown, AltDown, ControlDown; } game_input; inline game_controller_input *GetController(game_input *Input, int unsigned ControllerIndex) { Assert(ControllerIndex < ArrayCount(Input->Controllers)); game_controller_input *Result = &Input->Controllers[ControllerIndex]; return(Result); } inline b32 WasPressed(game_button_state State) { b32 Result = ((State.HalfTransitionCount > 1) || ((State.HalfTransitionCount == 1) && (State.EndedDown))); return(Result); } typedef struct platform_file_handle { b32 NoErrors; void *Platform; } platform_file_handle; typedef struct platform_file_group { u32 FileCount; void *Platform; } platform_file_group; typedef enum platform_file_type { PlatformFileType_AssetFile, PlatformFileType_SavedGameFile, PlatformFileType_Count, } platform_file_type; #define PLATFORM_GET_ALL_FILE_OF_TYPE_BEGIN(name) platform_file_group name(platform_file_type Type) typedef PLATFORM_GET_ALL_FILE_OF_TYPE_BEGIN(platform_get_all_files_of_type_begin); #define PLATFORM_GET_ALL_FILE_OF_TYPE_END(name) void name(platform_file_group *FileGroup) typedef PLATFORM_GET_ALL_FILE_OF_TYPE_END(platform_get_all_files_of_type_end); #define PLATFORM_OPEN_FILE(name) platform_file_handle name(platform_file_group *FileGroup) typedef PLATFORM_OPEN_FILE(platform_open_next_file); #define PLATFORM_READ_DATA_FROM_FILE(name) void name(platform_file_handle *Source, u64 Offset, u64 Size, void *Dest) typedef PLATFORM_READ_DATA_FROM_FILE(platform_read_data_from_file); #define PLATFORM_FILE_ERROR(name) void name(platform_file_handle *Handle, char *Message) typedef PLATFORM_FILE_ERROR(platform_file_error); #define PlatformNoFileErrors(Handle) ((Handle)->NoErrors) struct platform_work_queue; #define PLATFORM_WORK_QUEUE_CALLBACK(name) void name(platform_work_queue *Queue, void *Data) typedef PLATFORM_WORK_QUEUE_CALLBACK(platform_work_queue_callback); #define PLATFORM_ALLOCATE_MEMORY(name) void *name(memory_index Size) typedef PLATFORM_ALLOCATE_MEMORY(platform_allocate_memory); #define PLATFORM_DEALLOCATE_MEMORY(name) void name(void *Memory) typedef PLATFORM_DEALLOCATE_MEMORY(platform_deallocate_memory); typedef void platform_add_entry(platform_work_queue *Queue, platform_work_queue_callback *Callback, void *Data); typedef void platform_complete_all_work(platform_work_queue *Queue); typedef struct platform_api { platform_add_entry *AddEntry; platform_complete_all_work *CompleteAllWork; platform_get_all_files_of_type_begin *GetAllFilesOfTypeBegin; platform_get_all_files_of_type_end *GetAllFilesOfTypeEnd; platform_open_next_file *OpenNextFile; platform_read_data_from_file *ReadDataFromFile; platform_file_error *FileError; platform_allocate_memory *AllocateMemory; platform_deallocate_memory *DeallocateMemory; #if HANDMADE_INTERNAL debug_platform_free_file_memory *DEBUGFreeFileMemory; debug_platform_read_entire_file *DEBUGReadEntireFile; debug_platform_write_entire_file *DEBUGWriteEntireFile; debug_platform_execute_system_command *DEBUGExecuteSystemCommand; debug_platform_get_process_state *DEBUGGetProcessState; #endif debug_table * } platform_api; typedef struct game_memory { uint64 PermanentStorageSize; void *PermanentStorage; // NOTE(casey): REQUIRED to be cleared to zero at startup uint64 TransientStorageSize; void *TransientStorage; // NOTE(casey): REQUIRED to be cleared to zero at startup uint64 DebugStorageSize; void *DebugStorage; // NOTE(casey): REQUIRED to be cleared to zero at startup platform_work_queue *HighPriorityQueue; platform_work_queue *LowPriorityQueue; b32 ExecutableReloaded; platform_api PlatformAPI; } game_memory; #define GAME_UPDATE_AND_RENDER(name) void name(game_memory *Memory, game_input *Input, game_offscreen_buffer *Buffer) typedef GAME_UPDATE_AND_RENDER(game_update_and_render); // NOTE(casey): At the moment, this has to be a very fast function, it cannot be // more than a millisecond or so. // TODO(casey): Reduce the pressure on this function's performance by measuring it // or asking about it, etc. #define GAME_GET_SOUND_SAMPLES(name) void name(game_memory *Memory, game_sound_output_buffer *SoundBuffer) typedef GAME_GET_SOUND_SAMPLES(game_get_sound_samples); #if COMPILER_MSVC #define CompletePreviousReadsBeforeFutureReads _ReadBarrier() #define CompletePreviousWritesBeforeFutureWrites _WriteBarrier() inline uint32 AtomicCompareExchangeUInt32(uint32 volatile *Value, uint32 New, uint32 Expected) { uint32 Result = _InterlockedCompareExchange((long volatile *)Value, New, Expected); return(Result); } inline u64 AtomicExchangeU64(u64 volatile *Value, u64 New) { u64 Result = _InterlockedExchange64((__int64 volatile *)Value, New); return(Result); } inline u64 AtomicAddU64(u64 volatile *Value, u64 Addend) { // NOTE(casey): Returns the original value _prior_ to adding u64 Result = _InterlockedExchangeAdd64((__int64 volatile *)Value, Addend); return(Result); } inline u32 GetThreadID(void) { u8 *ThreadLocalStorage = (u8 *)__readgsqword(0x30); u32 ThreadID = *(u32 *)(ThreadLocalStorage + 0x48); return(ThreadID); } #elif COMPILER_LLVM // TODO(casey): Does LLVM have real read-specific barriers yet? #define CompletePreviousReadsBeforeFutureReads asm volatile("" ::: "memory") #define CompletePreviousWritesBeforeFutureWrites asm volatile("" ::: "memory") inline uint32 AtomicCompareExchangeUInt32(uint32 volatile *Value, uint32 New, uint32 Expected) { uint32 Result = __sync_val_compare_and_swap(Value, Expected, New); return(Result); } inline u64 AtomicExchangeU64(u64 volatile *Value, u64 New) { u64 Result = __sync_lock_test_and_set(Value, New); return(Result); } inline u64 AtomicAddU64(u64 volatile *Value, u64 Addend) { // NOTE(casey): Returns the original value _prior_ to adding u64 Result = __sync_fetch_and_add(Value, Addend); return(Result); } inline u32 GetThreadID(void) { u32 ThreadID; #if defined(__APPLE__) && defined(__x86_64__) asm("mov %%gs:0x00,%0" : "=r"(ThreadID)); #elif defined(__i386__) asm("mov %%gs:0x08,%0" : "=r"(ThreadID)); #elif defined(__x86_64__) asm("mov %%fs:0x10,%0" : "=r"(ThreadID)); #else #error Unsupported architecture #endif return(ThreadID); } #else // TODO(casey): Other compilers/platforms?? #endif #include "handmade_debug_interface.h" #define HANDMADE_PLATFORM_H #endif