/* This header is precompiled and automatically included into all source files */ /* NOTE: Include guards disabled since it breaks editor parsing */ //#ifndef COMMON_H //#define COMMON_H #ifdef __cplusplus extern "C" { #endif /* ========================== * * Compiler headers * ========================== */ /* Intrinsic header info: * MMX * SSE * SSE2 * SSE3 * SSSE3 * SSE4.1 * SSE4.2 * SSE4A * AES * AVX, AVX2, FMA */ #include #include #include #include #include /* SSE4.2 */ /* ========================== * * Flag defaults * ========================== */ /* Compile definition defaults */ #ifndef RTC # define RTC 0 #endif #ifndef ASAN # define ASAN 0 #endif #ifndef CRTLIB # define CRTLIB 0 #endif #ifndef DEBINFO # define DEBINFO 0 #endif #ifndef DEVELOPER # define DEVELOPER 0 #endif #ifndef PROFILING # define PROFILING 0 #endif #ifndef UNOPTIMIZED # define UNOPTIMIZED 0 #endif #ifndef RUN_TESTS # define RUN_TESTS 0 #endif /* ========================== * * Machine context * ========================== */ /* Compiler */ #if defined(__clang__) # define COMPILER_CLANG 1 # define COMPILER_MSVC 0 #elif defined(_MSC_VER) # define COMPILER_CLANG 0 # define COMPILER_MSVC 1 #else # error "Unknown compiler" #endif /* Operating system */ #if defined(_WIN32) # define PLATFORM_WINDOWS 1 # define PLATFORM_MAC 0 # define PLATFORM_LINUX 0 #elif defined(__APPLE__) && defined(__MACH__) # define PLATFORM_WINDOWS 0 # define PLATFORM_MAC 1 # define PLATFORM_LINUX 0 #elif defined(__gnu_linux__) # define PLATFORM_WINDOWS 0 # define PLATFORM_MAC 0 # define PLATFORM_LINUX 1 #else # error "Unknown OS" #endif #if defined(__cplusplus) # define LANGUAGE_CPP 1 # define LANGUAGE_C 0 #else # define LANGUAGE_CPP 0 # define LANGUAGE_C 1 #endif /* ========================== * * Debug * ========================== */ /* Compile time assert */ #define CT_ASSERT3(cond, line) struct CT_ASSERT_____##line {int foo[(cond) ? 1 : -1];} #define CT_ASSERT2(cond, line) CT_ASSERT3(cond, line) #define CT_ASSERT(cond) CT_ASSERT2(cond, __LINE__) #if COMPILER_MSVC # if DEBINFO # define DEBUG_ALIAS(var, alias) *(alias) = &(var); # else # define DEBUG_ALIAS(var, alias) *(alias) = &(var); # endif #else # if DEBINFO # define DEBUG_ALIAS(var, alias) __attribute((used)) *(alias) = &(var); # else # define DEBUG_ALIAS(var, alias) __attribute((unused)) *(alias) = &(var); # endif #endif #if RTC # if COMPILER_MSVC # define ASSERT(cond) ((cond) ? 1 : ((*(volatile int *)0) = 0, 0)) # define DEBUGBREAK __debugbreak # else # define ASSERT(cond) ((cond) ? 1 : (__builtin_trap(), 0)) # define DEBUGBREAK __builtin_debugtrap() # endif # define DEBUGBREAKABLE { volatile i32 __DEBUGBREAKABLE_VAR = 0; (UNUSED) __DEBUGBREAKABLE_VAR; } #else # define ASSERT(cond) (void)(0) #endif /* Address sanitization */ #if ASAN void __asan_poison_memory_region(void const volatile *, size_t); void __asan_unpoison_memory_region(void const volatile *add, size_t); # define ASAN_POISON(addr, size) __asan_poison_memory_region((addr), (size)); # define ASAN_UNPOISON(addr, size) __asan_unpoison_memory_region((addr), (size)); #else # define ASAN_POISON(addr, size) # define ASAN_UNPOISON(addr, size) #endif /* ========================== * * Common macros * ========================== */ #if COMPILER_MSVC && LANGUAGE_CPP # define CPPFRIENDLY_INITLIST_TYPE(type) type # else # define CPPFRIENDLY_INITLIST_TYPE(type) (type) #endif #if 1 # define INLINE static inline #else /* TODO: benchmark benefits of forced inlining */ # define INLINE static inline __attribute((always_inline)) #endif #if COMPILER_MSVC # define FORCE_INLINE static inline __forceinline #else # define FORCE_INLINE static inline __attribute((always_inline)) #endif /* Separate `static` usage into different keywords for easier grepping */ #define LOCAL_PERSIST static #define INTERNAL static #define GLOBAL static /* Read-only */ #if PLATFORM_WINDOWS # if COMPILER_MSVC # pragma section(".rdata$", read) # define READONLY __declspec(allocate(".rdata$")) # else # define READONLY __declspec(allocate(".rdata")) # endif #elif PLATFORM_MAC #define READONLY __attribute((section("__TEXT,__const"))) #else #define READONLY __attribute((section(".rodata"))) #endif /* Markup */ #define UNUSED void #if COMPILER_CLANG #define FALLTHROUGH __attribute((fallthrough)) #else #define FALLTHROUGH #endif /* Sizes */ #define KILOBYTE(n) (n*1024ULL) #define MEGABYTE(n) (n*KILOBYTE(1024ULL)) #define GIGABYTE(n) (n*MEGABYTE(1024ULL)) #define TERABYTE(n) (n*GIGABYTE(1024ULL)) /* typeof */ #if LANGUAGE_CPP || (__STDC_VERSION__ < 202311L) # if COMPILER_MSVC /* Typeof not supported in MSVC */ # define typeof(type) ASSERT(false) # define TYPEOF_DEFINED 0 # else # define typeof(type) __typeof__(type) # define TYPEOF_DEFINED 1 # endif #endif /* alignof */ #if LANGUAGE_C && (__STDC_VERSION__ < 202311L) # define alignof(type) __alignof(type) #endif #define ARRAY_COUNT(a) (sizeof(a) / sizeof((a)[0])) /* Field macros */ #define FIELD_SIZEOF(type, field) sizeof(((type *)0)->field) #if COMPILER_MSVC && !defined _CRT_USE_BUILTIN_OFFSETOF # define FIELD_OFFSETOF(type, field) ((u64)&(((type *)0)->field)) #else # define FIELD_OFFSETOF(type, field) __builtin_offsetof(type, field) #endif /* Bool */ #if !LANGUAGE_CPP # define true 1 # define false 0 #endif /* Array */ #define IS_INDEXABLE(a) (sizeof(a[0])) #define IS_ARRAY(a) (IS_INDEXABLE(a) && (((void *)&a) == ((void *)a))) /* Pack */ #if COMPILER_MSVC # define PACK(s) __pragma(pack(push, 1)) s __pragma(pack(pop)) #else # define PACK(s) s __attribute((__packed__)) #endif /* Color */ #define RGBA_32(r, g, b, a) (u32)((u32)(r) | ((u32)(g) << 8) | ((u32)(b) << 16) | ((u32)(a) << 24)) #define RGB_32(r, g, b) RGBA_32(r, g, b, 0xFF) #define _RGB_F_TO_U8(f) ((u8)((f * 255.0f) + 0.5f)) #define RGBA_32_F(r, g, b, a) RGBA_32(_RGB_F_TO_U8(r), _RGB_F_TO_U8(g), _RGB_F_TO_U8(b), _RGB_F_TO_U8(a)) #define RGB_32_F(r, g, b) RGBA_32_F(r, g, b, 1.f) #define COLOR_WHITE RGB_32(0xFF, 0xFF, 0xFF) #define COLOR_BLACK RGB_32(0, 0, 0 ) #define COLOR_RED RGB_32(0xFF, 0, 0 ) #define COLOR_GREEN RGB_32(0, 0xFF, 0 ) #define COLOR_BLUE RGB_32(0, 0, 0xFF) /* Barrier */ #if COMPILER_MSVC # define WRITE_BARRIER() _WriteBarrier() # define READ_BARRIER() _ReadBarrier() #elif defined(__x86_64) || defined(__i386__) # define WRITE_BARRIER() __asm__ volatile("" ::: "memory") # define READ_BARRIER() __asm__ volatile("" ::: "memory") #else # error "Memory barriers not implemented" #endif /* Cat */ #define CAT1(a, b) a ## b #define CAT(a, b) CAT1(a, b) #if 0 /* ========================== * * Bit utils * ========================== */ #define bit_set(i, mask) (i |= mask) #define bit_clear(i, mask) (i &= ~mask) #define bit_flip(i, mask) (i ^= mask) #endif /* ========================== * * Primitive types * ========================== */ typedef int8_t i8; typedef int16_t i16; typedef int32_t i32; typedef int64_t i64; typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; typedef uint64_t u64; typedef float f32; typedef double f64; typedef i8 b8; typedef i32 b32; #define U8_MAX (0xFF) #define U16_MAX (0xFFFF) #define U32_MAX (0xFFFFFFFF) #define U64_MAX (0xFFFFFFFFFFFFFFFFULL) #define I8_MAX (0x7F) #define I16_MAX (0x7FFF) #define I32_MAX (0x7FFFFFFF) #define I64_MAX (0x7FFFFFFFFFFFFFFFLL) #define I8_MIN ((i8)-0x80) #define I16_MIN ((i16)-0x8000) #define I32_MIN ((i32)-0x80000000) #define I64_MIN ((i64)-0x8000000000000000LL) GLOBAL const u32 _f32_infinity_u32 = 0x7f800000; GLOBAL const f32 *_f32_infinity = (f32 *)&_f32_infinity_u32; GLOBAL const u64 _f64_infinity_u64 = 0x7ff0000000000000ULL; GLOBAL const f64 *_f64_infinity = (f64 *)&_f64_infinity_u64; #define F32_INFINITY (*_f32_infinity) #define F64_INFINITY (*_f64_infinity) #define PI ((f32)3.14159265358979323846) #define TAU ((f32)6.28318530717958647693) /* ========================== * * Big int * ========================== */ #if COMPILER_CLANG #define U128(hi64, lo64) (((u128)(hi64) << 64) | (lo64)) #define U128_HI64(a) ((u64)((a) >> 64)) #define U128_LO64(a) ((u64)(a)) typedef __uint128_t u128; INLINE b32 u128_eq(u128 a, u128 b) { return a == b; } INLINE u128 u128_xor_u8(u128 a, u8 b) { return a ^ b; } INLINE u128 u128_mul(u128 a, u128 b) { return a * b; } #elif COMPILER_MSVC #if LANGUAGE_CPP # define U128(hi64, lo64) { (hi64), (lo64) } #else # define U128(hi64, lo64) ((u128) { .hi = (hi64), .lo = (lo64) }) #endif #define U128_HI64(a) ((a).hi) #define U128_LO64(a) ((a).lo) typedef struct { u64 hi; u64 lo; } u128; INLINE b32 u128_eq(u128 a, u128 b) { return a.hi == b.hi && a.lo == b.lo; } INLINE u128 u128_xor_u8(u128 a, u8 b) { return U128(a.hi, a.lo ^ b); } /* https://www.codeproject.com/Tips/784635/UInt-Bit-Operations */ u128 u128_mul(u128 a, u128 b) { u64 a1 = (a.lo & 0xffffffff); u64 b1 = (b.lo & 0xffffffff); u64 t = (a1 * b1); u64 w3 = (t & 0xffffffff); u64 k = (t >> 32); a.lo >>= 32; t = (a.lo * b1) + k; k = (t & 0xffffffff); u64 w1 = (t >> 32); b.lo >>= 32; t = (a1 * b.lo) + k; k = (t >> 32); u128 res = U128((a.lo * b.lo) + w1 + k, (t << 32) + w3); res.hi += (a.hi * b.lo) + (a.lo * b.hi); return res; } #endif /* ========================== * * Atomics * ========================== */ struct atomic_i32 { volatile i32 _v; }; struct atomic_i64 { volatile i64 _v; }; struct atomic_u32 { volatile u32 _v; }; struct atomic_u64 { volatile u64 _v; }; struct atomic_ptr { volatile void *_v; }; /* ========================== * * Common structs * ========================== */ struct arena { u64 pos; u64 committed; u64 reserved; u8 *base; #if RTC b32 readonly; #endif }; struct string { u64 len; u8 *text; }; struct string16 { u64 len; u16 *text; }; struct string32 { u64 len; u32 *text; }; struct image_rgba { u32 width; u32 height; u32 *pixels; /* Array of [width * height] pixels */ }; struct pcm { u64 count; i16 *samples; }; struct buffer { u64 size; u8 *data; }; struct renderer_handle { u64 v[1]; }; /* ========================== * * Tag structs * ========================== */ struct sprite_tag { u128 hash; struct string path; }; /* ========================== * * Buffer utils * ========================== */ /* Utility buffer constructor */ #define BUFFER(size, data) (CPPFRIENDLY_INITLIST_TYPE(struct buffer) { (size), (data) }) /* Utility buffer constructor from static array */ #define BUFFER_FROM_ARRAY(a) \ ( \ /* Must be array */ \ ASSERT(IS_ARRAY(a)), \ /* Must be array of bytes */ \ ASSERT(sizeof((a)[0]) == sizeof(u8)), \ ((struct buffer) { .size = ARRAY_COUNT(a), .data = (u8 *)(a) }) \ ) #define BUFFER_FROM_STRING(str) (CPPFRIENDLY_INITLIST_TYPE(struct buffer) { (str).len, (str).text }) #define BUFFER_FROM_POINTERS(p1, p2) (CPPFRIENDLY_INITLIST_TYPE(struct buffer) { (u8 *)(p2) - (u8 *)(p1), (u8 *)p1 }) #define BUFFER_FROM_STRUCT(ptr) (CPPFRIENDLY_INITLIST_TYPE(struct buffer) { sizeof(*(ptr)), (ptr) }) /* ========================== * * String utils * ========================== */ /* Expand C string literal with size for string initialization */ #define STR(cstr_lit) CPPFRIENDLY_INITLIST_TYPE(struct string) { (sizeof((cstr_lit)) - 1), (u8 *)(cstr_lit) } /* Same as `STR`, but works with static variable initialization */ #define STR_NOCAST(cstr_lit) { .len = (sizeof((cstr_lit)) - 1), .text = (u8 *)(cstr_lit) } #define STRING_FROM_BUFFER(buff) (CPPFRIENDLY_INITLIST_TYPE(struct string) { buff.size, buff.data }) #define STRING_FROM_ARRAY(a) STRING_FROM_BUFFER(BUFFER_FROM_ARRAY(a)) /* ========================== * * Math types * ========================== */ #define V2(x, y) ((struct v2) { (x), (y) }) struct v2 { f32 x, y; }; struct v2_array { struct v2 *points; u64 count; }; #define V3(x, y, z) ((struct v3) { (x), (y), (z) }) struct v3 { f32 x, y, z; }; struct v3_array { struct v3 *points; u64 count; }; #define V4(x, y, z, w) ((struct v4) { (x), (y), (z), (w) }) struct v4 { f32 x, y, z, w; }; struct v4_array { struct v4 *points; u64 count; }; struct xform { struct v2 bx; /* X basis vector (x axis) */ struct v2 by; /* Y basis vector (y axis)*/ struct v2 og; /* Translation vector (origin) */ }; struct mat4x4 { f32 e[4][4]; }; #define RECT(_x, _y, _width, _height) (struct rect) { .x = (_x), .y = (_y), .width = (_width), .height = (_height) } #define RECT_FROM_V2(_pos, _size) (struct rect) { .pos = (_pos), .size = (_size) } struct rect { union { struct { f32 x, y, width, height; }; struct { struct v2 pos, size; }; }; }; INLINE b32 rect_eq(struct rect r1, struct rect r2) { return r1.x == r2.x && r1.y == r2.y && r1.width == r2.width && r1.height == r2.height; } /* Values expected to be normalized 0.0 -> 1.0 */ #define CLIP_ALL ((struct clip_rect) { { 0.0f, 0.0f }, { 1.0f, 1.0f } }) struct clip_rect { struct v2 p1, p2; }; #define QUAD_UNIT_SQUARE (struct quad) { V2(0, 0), V2(0, 1), V2(1, 1), V2(1, 0) } #define QUAD_UNIT_SQUARE_CENTERED (struct quad) { V2(-0.5f, -0.5f), V2(0.5f, -0.5f), V2(0.5f, 0.5f), V2(-0.5f, 0.5f) } struct quad { struct v2 p1, p2, p3, p4; }; /* (T)ranslation, (R)otation, (S)cale */ #define TRS(...) ((struct trs) { .t = V2(0,0), .s = V2(1, 1), .r = 0, __VA_ARGS__ }) struct trs { struct v2 t; struct v2 s; f32 r; }; /* ========================== * * Common utilities * ========================== */ INLINE u8 min_u8(u8 a, u8 b) { return a <= b ? a : b; } INLINE u8 max_u8(u8 a, u8 b) { return a >= b ? a : b; } INLINE u32 min_u32(u32 a, u32 b) { return a <= b ? a : b; } INLINE u32 max_u32(u32 a, u32 b) { return a >= b ? a : b; } INLINE u64 min_u64(u64 a, u64 b) { return a <= b ? a : b; } INLINE u64 max_u64(u64 a, u64 b) { return a >= b ? a : b; } INLINE i32 min_i32(i32 a, i32 b) { return a <= b ? a : b; } INLINE i32 max_i32(i32 a, i32 b) { return a >= b ? a : b; } INLINE i64 min_i64(i64 a, i64 b) { return a <= b ? a : b; } INLINE i64 max_i64(i64 a, i64 b) { return a >= b ? a : b; } INLINE f32 min_f32(f32 a, f32 b) { return a <= b ? a : b; } INLINE f32 max_f32(f32 a, f32 b) { return a >= b ? a : b; } INLINE f64 min_f64(f64 a, f64 b) { return a <= b ? a : b; } INLINE f64 max_f64(f64 a, f64 b) { return a >= b ? a : b; } INLINE u32 clamp_u32(u32 v, u32 min, u32 max) { return v < min ? min : v > max ? max : v; } INLINE u64 clamp_u64(u64 v, u64 min, u64 max) { return v < min ? min : v > max ? max : v; } INLINE i32 clamp_i32(i32 v, i32 min, i32 max) { return v < min ? min : v > max ? max : v; } INLINE i64 clamp_i64(i64 v, i64 min, i64 max) { return v < min ? min : v > max ? max : v; } INLINE f32 clamp_f32(f32 v, f32 min, f32 max) { return v < min ? min : v > max ? max : v; } INLINE f64 clamp_f64(f64 v, f64 min, f64 max) { return v < min ? min : v > max ? max : v; } /* ========================== * * Profiling * ========================== */ #if PROFILING #include "third_party/tracy/tracy/TracyC.h" #define PROFILING_CAPTURE_FRAME_IMAGE 1 /* Clang/GCC cleanup macros */ #if COMPILER_MSVC # error "MSVC not supported for profiling (cleanup attributes are required for profiling markup)" #else # ifdef TRACY_NO_CALLSTACK # define __prof static const struct ___tracy_source_location_data CAT(__tracy_source_location,__LINE__) = { NULL, __func__, __FILE__, (uint32_t)__LINE__, 0 }; __attribute((cleanup(__prof_zone_cleanup_func))) TracyCZoneCtx __tracy_ctx = ___tracy_emit_zone_begin( &CAT(__tracy_source_location,__LINE__), true ); # define __profscope(name) static const struct ___tracy_source_location_data CAT(__tracy_source_location,__LINE__) = { NULL, #name, __FILE__, (uint32_t)__LINE__, 0 }; __attribute((cleanup(__prof_zone_cleanup_func))) TracyCZoneCtx __tracy_ctx = ___tracy_emit_zone_begin( &CAT(__tracy_source_location,__LINE__), true ); # else # define __prof static const struct ___tracy_source_location_data CAT(__tracy_source_location,__LINE__) = { NULL, __func__, __FILE__, (uint32_t)__LINE__, 0 }; __attribute((cleanup(__prof_zone_cleanup_func))) TracyCZoneCtx __tracy_ctx = ___tracy_emit_zone_begin_callstack( &CAT(__tracy_source_location,__LINE__), TRACY_CALLSTACK, true ); # define __profscope(name) static const struct ___tracy_source_location_data CAT(__tracy_source_location,__LINE__) = { NULL, #name, __FILE__, (uint32_t)__LINE__, 0 }; __attribute((cleanup(__prof_zone_cleanup_func))) TracyCZoneCtx __tracy_ctx = ___tracy_emit_zone_begin_callstack( &CAT(__tracy_source_location,__LINE__), TRACY_CALLSTACK, true ); # endif #endif INLINE void __prof_zone_cleanup_func(TracyCZoneCtx *__tracy_ctx) { TracyCZoneEnd(*__tracy_ctx); } #define __profalloc(ptr, size) TracyCAlloc(ptr, size) #define __proffree(ptr) TracyCFree(ptr) #define __profmsg(txt, len, col) TracyCMessageC(txt, len, col); #define __profframe(name) TracyCFrameMarkNamed(name) #if PROFILING_CAPTURE_FRAME_IMAGE # define __profframeimage(image, width, height, offset, flipped) TracyCFrameImage(image, width, height, offset, flipped); #else # define __profframeimage(image, width, height, offset, flipped) #endif /* PROFILING_CAPTURE_FRAME_IMAGE */ #else #define PROFILING_CAPTURE_FRAME_IMAGE 0 #define __prof #define __profscope(name) #define __profalloc(ptr, size) #define __proffree(ptr) #define __profmsg(txt, len, col) #define __profframe(name) #define __profframeimage(image, width, height, offset, flipped) #endif /* PROFILING */ /* ========================== * * Configurable constants * ========================== */ #include "config.h" #ifdef __cplusplus } #endif