ogl_beamforming

Ultrasound Beamforming Implemented with OpenGL
git clone anongit@rnpnr.xyz:ogl_beamforming.git
Log | Files | Refs | Feed | Submodules | README | LICENSE

Commit: c098f068120b3995bd7e4b45490833316388bd50
Parent: 60ffc2ebdd32377b74119d1e0cfcbd82109cc542
Author: Randy Palamar
Date:   Sat,  1 Aug 2026 14:44:15 -0700

base: make platform setup uniform between all programs in repo

There are certain platform properties that all programs want.
Instead of having to go back and forth modifying stuff all over
unify the setup.

Diffstat:
Rcompiler.h -> base_compiler.h | 0
Abase_intrinsics.h | 256+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Abase_linux.c | 231+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Abase_platform.h | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Abase_types.h | 170+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Abase_win32.c | 321+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mbeamformer.h | 57+++++++--------------------------------------------------
Mbeamformer_core.c | 13++++++++-----
Mbuild.c | 48+++++++++++++++++++++++++++++-------------------
Mexternal/glslang_local/glslang.cpp | 2+-
Dintrinsics.c | 234-------------------------------------------------------------------------------
Mlib/ogl_beamformer_lib.c | 11+++++++----
Mmain_linux.c | 45+++++++++++++++++++++------------------------
Mmain_w32.c | 59++++++++++++++++++++++++++---------------------------------
Dos_linux.c | 216-------------------------------------------------------------------------------
Dos_win32.c | 303-------------------------------------------------------------------------------
Mtests/decode.c | 11++++++-----
Mtests/throughput.c | 9+++++----
Mutil.c | 18------------------
Mutil.h | 189+------------------------------------------------------------------------------
20 files changed, 1157 insertions(+), 1104 deletions(-)

diff --git a/compiler.h b/base_compiler.h diff --git a/base_intrinsics.h b/base_intrinsics.h @@ -0,0 +1,256 @@ +/* See LICENSE for license details. */ +#ifndef BASE_INTRINSICS_H +#define BASE_INTRINSICS_H + +#include "base_compiler.h" +#include "base_types.h" + +#define function static +#define global static +#define local_persist static + +#ifndef asm +#define asm __asm__ +#endif + +#ifndef typeof +#define typeof __typeof__ +#endif + +#if COMPILER_CLANG || COMPILER_GCC + #define force_inline inline __attribute__((always_inline)) +#elif COMPILER_MSVC + #define force_inline __forceinline +#endif + +#if COMPILER_MSVC || (COMPILER_CLANG && OS_WINDOWS) + #pragma section(".rdata$", read) + #define read_only __declspec(allocate(".rdata$")) +#elif COMPILER_CLANG + #define read_only __attribute__((section(".rodata"))) +#elif COMPILER_GCC + /* TODO(rnp): how do we do this with gcc, putting it in rodata causes warnings and writing to + * it doesn't cause a fault */ + #define read_only +#endif + +#if !defined(countof) + #define countof(a) (i64)(sizeof(a) / sizeof(*a)) +#endif + +#if COMPILER_MSVC + #define alignas(n) __declspec(align(n)) + #define pack_struct(s) __pragma(pack(push, 1)) s __pragma(pack(pop)) + #define no_return __declspec(noreturn) + + #define likely(x) (x) + #define unlikely(x) (x) + + #define print_format(f, va) + + #define assume(x) __assume(x) + #define debugbreak() __debugbreak() + #define unreachable() __assume(0) + + #if ARCH_ARM64 + #define cpu_yield() __yield() + #define store_fence() __dmb(0x0A) // 0x0A: ishst + #endif + + #define atomic_add_u32(ptr, n) _InterlockedExchangeAdd((volatile u32 *)(ptr), (n)) + #define atomic_add_u64(ptr, n) _InterlockedExchangeAdd64((volatile u64 *)(ptr), (n)) + #define atomic_and_u32(ptr, n) _InterlockedAnd((volatile u32 *)(ptr), (n)) + #define atomic_and_u64(ptr, n) _InterlockedAnd64((volatile u64 *)(ptr), (n)) + #define atomic_cas_u32(ptr, cptr, n) (_InterlockedCompareExchange((volatile u32 *)(ptr), *(cptr), (n)) == *(cptr)) + #define atomic_cas_u64(ptr, cptr, n) (_InterlockedCompareExchange64((volatile u64 *)(ptr), *(cptr), (n)) == *(cptr)) + #define atomic_load_u32(ptr) *((volatile u32 *)(ptr)) + #define atomic_load_u64(ptr) *((volatile u64 *)(ptr)) + #define atomic_or_u32(ptr, n) _InterlockedOr((volatile u32 *)(ptr), (n)) + #define atomic_store_u32(ptr, n) *((volatile u32 *)(ptr)) = (u32)(n) + #define atomic_store_u64(ptr, n) *((volatile u64 *)(ptr)) = (u64)(n) + #define atomic_swap_u32(ptr, n) _InterlockedExchange((volatile u32 *)(ptr), n) + #define atomic_swap_u64(ptr, n) _InterlockedExchange64((volatile u64 *)(ptr), n) + + #define atan2_f32(y, x) atan2f(y, x) + #define cos_f32(a) cosf(a) + #define sin_f32(a) sinf(a) + #define tan_f32(a) tanf(a) + #define ceil_f32(a) ceilf(a) + #define sqrt_f32(a) sqrtf(a) + + #define exp_f64(a) exp(a) + #define sqrt_f64(a) sqrt(a) + +#else + #define alignas(n) __attribute__((aligned(n))) + #define pack_struct(s) s __attribute__((packed)) + #define no_return __attribute__((noreturn)) + + #define likely(x) (__builtin_expect(!!(x), 1)) + #define unlikely(x) (__builtin_expect(!!(x), 0)) + + #define print_format(f, va) __attribute__((format(printf, f, va))) + + #if COMPILER_CLANG + #define assume(x) __builtin_assume(x) + #else + #if defined(__has_attribute) + #if __has_attribute(assume) + #define assume(x) __attribute__((assume(x))) + #endif + #endif + #endif + #if !defined(assume) + #define assume(x) if (!(x)) unreachable() + #endif + #define unreachable() __builtin_unreachable() + #if ARCH_ARM64 + /* TODO? debuggers just loop here forever and need a manual PC increment (step over) */ + #define debugbreak() asm volatile ("brk 0xf000") + #define cpu_yield() asm volatile ("yield") + #define store_fence() asm volatile ("dmb ishst" ::: "memory") + #else + #define debugbreak() asm volatile ("int3; nop") + #endif + + #define atomic_add_u64(ptr, n) __atomic_fetch_add(ptr, n, __ATOMIC_SEQ_CST) + #define atomic_and_u64(ptr, n) __atomic_and_fetch(ptr, n, __ATOMIC_SEQ_CST) + #define atomic_cas_u64(ptr, cptr, n) __atomic_compare_exchange_n(ptr, cptr, n, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) + #define atomic_load_u64(ptr) __atomic_load_n(ptr, __ATOMIC_SEQ_CST) + #define atomic_or_u32(ptr, n) __atomic_or_fetch(ptr, n, __ATOMIC_SEQ_CST) + #define atomic_store_u64(ptr, n) __atomic_store_n(ptr, n, __ATOMIC_SEQ_CST) + #define atomic_swap_u64(ptr, n) __atomic_exchange_n(ptr, n, __ATOMIC_SEQ_CST) + #define atomic_add_u32 atomic_add_u64 + #define atomic_and_u32 atomic_and_u64 + #define atomic_cas_u32 atomic_cas_u64 + #define atomic_load_u32 atomic_load_u64 + #define atomic_store_u32 atomic_store_u64 + #define atomic_swap_u32 atomic_swap_u64 + + #define atan2_f32(y, x) __builtin_atan2f(y, x) + #define cos_f32(a) __builtin_cosf(a) + #define sin_f32(a) __builtin_sinf(a) + #define tan_f32(a) __builtin_tanf(a) + #define ceil_f32(a) __builtin_ceilf(a) + #define sqrt_f32(a) __builtin_sqrtf(a) + + #define exp_f64(a) __builtin_exp(a) + #define sqrt_f64(a) __builtin_sqrt(a) + + #define popcount_u64(a) (u64)__builtin_popcountll(a) +#endif + +#define KB(a) ((u64)(a) << 10ULL) +#define MB(a) ((u64)(a) << 20ULL) +#define GB(a) ((u64)(a) << 30ULL) + +#if COMPILER_MSVC + +function force_inline u64 +clz_u64(u64 a) +{ + u64 result = 64, index; + if (a) { + _BitScanReverse64(&index, a); + result = index; + } + return result; +} + +function force_inline u64 +ctz_u64(u64 a) +{ + u64 result = 64, index; + if (a) { + _BitScanForward64(&index, a); + result = index; + } + return result; +} + +#else /* !COMPILER_MSVC */ + +function force_inline u64 +clz_u64(u32 a) +{ + u64 result = 64; + if (a) result = (u64)__builtin_clzll(a); + return result; +} + +function force_inline u64 +ctz_u64(u64 a) +{ + u64 result = 64; + if (a) result = (u64)__builtin_ctzll(a); + return result; +} + +#endif + +#if ARCH_ARM64 +/* NOTE(rnp): we are only doing a handful of f32x4 operations so we will just use NEON and do + * the macro renaming thing. If you are implementing a serious wide vector operation you should + * use SVE(2) instead. The semantics are different however and the code will be written for an + * arbitrary vector bit width. In that case you will also need x86_64 code for determining + * the supported vector width (ideally at runtime though that may not be possible). + */ +#include <arm_neon.h> +typedef float32x4_t f32x4; +typedef int32x4_t i32x4; +typedef uint32x4_t u32x4; + +#define add_f32x4(a, b) vaddq_f32(a, b) +#define cvt_i32x4_f32x4(a) vcvtq_f32_s32(a) +#define cvt_f32x4_i32x4(a) vcvtq_s32_f32(a) +#define div_f32x4(a, b) vdivq_f32(a, b) +#define dup_f32x4(f) vdupq_n_f32(f) +#define floor_f32x4(a) vrndmq_f32(a) +#define load_f32x4(a) vld1q_f32(a) +#define load_i32x4(a) vld1q_s32(a) +#define max_f32x4(a, b) vmaxq_f32(a, b) +#define min_f32x4(a, b) vminq_f32(a, b) +#define mul_f32x4(a, b) vmulq_f32(a, b) +#define set_f32x4(a, b, c, d) vld1q_f32((f32 []){d, c, b, a}) +#define sqrt_f32x4(a) vsqrtq_f32(a) +#define store_f32x4(o, a) vst1q_f32(o, a) +#define store_i32x4(o, a) vst1q_s32(o, a) +#define sub_f32x4(a, b) vsubq_f32(a, b) + +#elif ARCH_X64 +#include <immintrin.h> +typedef __m128 f32x4; +typedef __m128i i32x4; +typedef __m128i u32x4; + +#define add_f32x4(a, b) _mm_add_ps(a, b) +#define cvt_i32x4_f32x4(a) _mm_cvtepi32_ps(a) +#define cvt_f32x4_i32x4(a) _mm_cvtps_epi32(a) +#define div_f32x4(a, b) _mm_div_ps(a, b) +#define dup_f32x4(f) _mm_set1_ps(f) +#define floor_f32x4(a) _mm_floor_ps(a) +#define load_f32x4(a) _mm_loadu_ps(a) +#define load_i32x4(a) _mm_loadu_si128((i32x4 *)a) +#define max_f32x4(a, b) _mm_max_ps(a, b) +#define min_f32x4(a, b) _mm_min_ps(a, b) +#define mul_f32x4(a, b) _mm_mul_ps(a, b) +#define set_f32x4(a, b, c, d) _mm_set_ps(a, b, c, d) +#define sqrt_f32x4(a) _mm_sqrt_ps(a) +#define store_f32x4(o, a) _mm_storeu_ps(o, a) +#define store_i32x4(o, a) _mm_storeu_si128((i32x4 *)o, a) +#define sub_f32x4(a, b) _mm_sub_ps(a, b) + +#define cpu_yield _mm_pause +#define store_fence _mm_sfence + +#endif + +function force_inline f32 +inf32(void) +{ + union {u32 u; f32 f;} result; + result.u = 0x7F800000u; + return result.f; +} + +#endif /* BASE_INTRINSICS_H */ diff --git a/base_linux.c b/base_linux.c @@ -0,0 +1,231 @@ +/* See LICENSE for license details. */ + +/* NOTE(rnp): provides the platform layer for everything in this repo. */ + +#define OS_SHARED_MEMORY_NAME "/ogl_beamformer_shared_memory" + +#define OS_PATH_SEPARATOR_CHAR '/' +#define OS_PATH_SEPARATOR "/" + +#include "base_platform.h" + +#include "util.h" + +#include <errno.h> +#include <fcntl.h> +#include <linux/futex.h> +#include <poll.h> +#include <pthread.h> +#include <sys/auxv.h> +#include <sys/inotify.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/syscall.h> +#include <sys/sysinfo.h> +#include <unistd.h> + +global OSSystemInfo linux_system_info; + +function b32 +os_write_file(i32 file, void *data, i64 length) +{ + i64 offset = 0; + while (offset < length) { + i64 r = write(file, (u8 *)data + offset, (u64)(length - offset)); + if (r < 0 && errno != EINTR) break; + if (r >= 0) offset += r; + } + return offset == length; +} + +BASE_EXPORT no_return void +os_exit(i32 code) +{ + _exit(code); + unreachable(); +} + +function u64 +os_timer_frequency(void) +{ + return 1000000000ULL; +} + +BASE_EXPORT u64 +os_timer_count(void) +{ + struct timespec time = {0}; + clock_gettime(CLOCK_MONOTONIC, &time); + u64 result = (u64)time.tv_sec * 1000000000ULL + (u64)time.tv_nsec; + return result; +} + +function u64 +os_number_of_processors(void) +{ + u64 set[128 / sizeof(u64)] = {0}; + syscall(SYS_sched_getaffinity, 0, sizeof(set), set); + + u64 result = 0; + for EachElement(set, it) + result += popcount_u64(set[it]); + return result > 0 ? result : 1; +} + +function void +os_system_info_init(void) +{ + linux_system_info.timer_frequency = os_timer_frequency(); + linux_system_info.logical_processor_count = os_number_of_processors(); + linux_system_info.page_size = ARCH_X64? KB(4) : getauxval(AT_PAGESZ); + linux_system_info.path_separator_byte = '/'; +} + +BASE_EXPORT OSSystemInfo * +os_system_info(void) +{ + #if BASE_PLATFORM_NO_MAIN + if unlikely(linux_system_info.path_separator_byte == 0) + os_system_info_init(); + #endif + return &linux_system_info; +} + +BASE_EXPORT void * +os_memory_reserve(u64 size) +{ + void *result = mmap(0, size, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); + if (result == MAP_FAILED) + result = 0; + return result; +} + +BASE_EXPORT void +os_memory_release(void *base, u64 size) +{ + munmap(base, size); +} + +BASE_EXPORT b32 +os_memory_commit(void *base, u64 size) +{ + mprotect(base, size, PROT_READ|PROT_WRITE); + return 1; +} + +BASE_EXPORT void +os_memory_uncommit(void *base, u64 size) +{ + madvise(base, size, MADV_DONTNEED); + mprotect(base, size, PROT_NONE); +} + +BASE_EXPORT void +os_memory_seal(void *base, u64 size) +{ + mprotect(base, size, PROT_READ); +} + +BASE_EXPORT str8 +os_read_entire_file(Arena *arena, const char *file) +{ + str8 result = {0}; + struct stat sb; + i32 fd = open(file, O_RDONLY); + if (fd >= 0 && fstat(fd, &sb) >= 0) { + result.data = push_array(arena, u8, sb.st_size); + do { + i64 rlen = read(fd, result.data + result.length, (u64)(sb.st_size - result.length)); + if (rlen > 0) result.length += rlen; + } while (result.length != sb.st_size && errno != EINTR); + if (result.length != sb.st_size) { + arena_pop(arena, sb.st_size); + zero_struct(&result); + } + } + if (fd >= 0) close(fd); + + return result; +} + +function b32 +os_write_new_file(char *fname, str8 raw) +{ + b32 result = 0; + i32 fd = open(fname, O_WRONLY|O_TRUNC|O_CREAT, 0600); + if (fd != INVALID_FILE) { + result = os_write_file(fd, raw.data, raw.length); + close(fd); + } + return result; +} + +function b32 +os_file_exists(char *path) +{ + struct stat st; + b32 result = stat(path, &st) == 0; + return result; +} + +/* NOTE: complete garbage because there is no standarized copyfile() in POSix */ +function b32 +os_copy_file(char *name, char *new) +{ + b32 result = 0; + struct stat sb; + if (stat(name, &sb) == 0) { + i32 fd_old = open(name, O_RDONLY); + i32 fd_new = open(new, O_WRONLY|O_CREAT, sb.st_mode); + if (fd_old >= 0 && fd_new >= 0) { + u8 buf[4096]; + i64 copied = 0; + while (copied != sb.st_size) { + i64 r = read(fd_old, buf, countof(buf)); + if (r < 0) break; + i64 w = write(fd_new, buf, (u64)r); + if (w < 0) break; + copied += w; + } + result = copied == sb.st_size; + } + if (fd_old != -1) close(fd_old); + if (fd_new != -1) close(fd_new); + } + return result; +} + +BASE_EXPORT b32 +os_wait_on_address(i32 *value, i32 current, u32 timeout_ms) +{ + struct timespec *timeout = 0, timeout_value; + if (timeout_ms != (u32)-1) { + timeout_value.tv_sec = timeout_ms / 1000; + timeout_value.tv_nsec = (timeout_ms % 1000) * 1000000; + timeout = &timeout_value; + } + return syscall(SYS_futex, value, FUTEX_WAIT, current, timeout, 0, 0) == 0; +} + +BASE_EXPORT void +os_wake_all_waiters(i32 *sync) +{ + if (sync) { + atomic_store_u32(sync, 0); + syscall(SYS_futex, sync, FUTEX_WAKE, I32_MAX, 0, 0, 0); + } +} + +#if !BASE_PLATFORM_NO_MAIN +BASE_IMPORT void entry_point(i32 argc, char *argv[]); + +extern i32 +main(i32 argc, char *argv[]) +{ + os_system_info_init(); + + entry_point(argc, argv); + + return 0; +} +#endif diff --git a/base_platform.h b/base_platform.h @@ -0,0 +1,68 @@ +/* See LICENSE for license details. */ +#ifndef BASE_PLATFORM_H +#define BASE_PLATFORM_H + +#ifndef BASE_EXPORT +#define BASE_EXPORT +#endif + +#ifndef BASE_IMPORT +#define BASE_IMPORT +#endif + +#ifndef BASE_PLATFORM_NO_MAIN +#define BASE_PLATFORM_NO_MAIN 0 +#endif + +#include "base_types.h" +#include "base_intrinsics.h" + +#define OSInvalidHandleValue ((u64)-1) +typedef struct { u64 value[1]; } OSBarrier; +typedef struct { u64 value[1]; } OSHandle; +typedef struct { u64 value[1]; } OSLibrary; +typedef struct { u64 value[1]; } OSThread; +typedef struct { u64 value[1]; } OSWindow; +typedef struct { u64 value[1]; } OSW32Semaphore; + +typedef u64 os_thread_entry_point_fn(void *user_context); + +typedef struct { + u64 timer_frequency; + + u32 logical_processor_count; + u32 page_size; + + u8 path_separator_byte; +} OSSystemInfo; + +BASE_EXPORT OSSystemInfo * os_system_info(void); + +BASE_EXPORT void no_return os_exit(i32 code); + +BASE_EXPORT void * os_memory_reserve(u64 size); +BASE_EXPORT void os_memory_release(void *base, u64 size); +BASE_EXPORT u32 os_memory_commit(void *base, u64 size); +BASE_EXPORT void os_memory_uncommit(void *base, u64 size); +BASE_EXPORT void os_memory_seal(void *base, u64 size); + +BASE_EXPORT u64 os_timer_count(void); + +BASE_EXPORT str8 os_read_entire_file(Arena *arena, const char *file); + +/* NOTE(rnp): memory watch timed waiting functions. (-1) is an infinite timeout. + * Used with the intention of yielding the thread back to the OS. */ +BASE_EXPORT u32 os_wait_on_address(i32 *lock, i32 current, u32 timeout_ms); +BASE_EXPORT void os_wake_all_waiters(i32 *lock); + + +/* NOTE(rnp): this functionality is only needed on win32 to provide cross process + * synchronization. While posix has equivalent functionality there is no reason to + * use it over a value located in shared memory. */ +#if OS_WINDOWS +BASE_EXPORT OSW32Semaphore os_w32_create_semaphore(const char *name, i32 initial_count, i32 maximum_count); +BASE_EXPORT u32 os_w32_semaphore_wait(OSW32Semaphore, u32 timeout_ms); +BASE_EXPORT void os_w32_semaphore_release(OSW32Semaphore, i32 count); +#endif + +#endif /* BASE_PLATFORM_H */ diff --git a/base_types.h b/base_types.h @@ -0,0 +1,170 @@ +#ifndef BASE_TYPES_H +#define BASE_TYPES_H +#include "base_compiler.h" + +#if COMPILER_MSVC + typedef unsigned __int64 u64; + typedef signed __int64 i64; + typedef unsigned __int32 u32; + typedef signed __int32 i32; + typedef unsigned __int16 u16; + typedef signed __int16 i16; + typedef unsigned __int8 u8; + typedef signed __int8 i8; +#else + typedef __UINT64_TYPE__ u64; + typedef __INT64_TYPE__ i64; + typedef __UINT32_TYPE__ u32; + typedef __INT32_TYPE__ i32; + typedef __UINT16_TYPE__ u16; + typedef __INT16_TYPE__ i16; + typedef __UINT8_TYPE__ u8; + typedef __INT8_TYPE__ i8; +#endif + +#define I8_MAX (0x0000007FL) +#define I32_MAX (0x7FFFFFFFL) +#define S32_MAX (0x7FFFFFFFL) +#define U8_MAX (0x000000FFUL) +#define U16_MAX (0x0000FFFFUL) +#define U32_MAX (0xFFFFFFFFUL) +#define U64_MAX (0xFFFFFFFFFFFFFFFFULL) +#define F32_EPSILON (1e-6f) +#ifndef PI + #define PI (3.14159265358979323846f) +#endif + +typedef char c8; +typedef u8 b8; +typedef u16 b16; +typedef u32 b32; +typedef _Float16 f16; +typedef float f32; +typedef double f64; +typedef i64 iptr; +typedef u64 uptr; + +typedef struct { u64 start, stop; } RangeU64; + +typedef union { + struct { i32 x, y; }; + struct { i32 w, h; }; + i32 E[2]; +} iv2; + +typedef union { + struct { i32 x, y, z; }; + struct { i32 w, h, d; }; + iv2 xy; + i32 E[3]; +} iv3; + +typedef union { + struct { i32 x, y, z, w; }; + struct { iv3 xyz; i32 _w; }; + i32 E[4]; +} iv4; + +typedef union { + struct { u32 x, y; }; + struct { u32 w, h; }; + u32 E[2]; +} uv2; + +typedef union { + struct { u32 x, y, z; }; + struct { u32 w, h, d; }; + uv2 xy; + u32 E[3]; +} uv3; + +typedef union { + struct { u32 x, y, z, w; }; + struct { uv3 xyz; u32 _w; }; + u32 E[4]; +} uv4; + +typedef union { + struct { b32 x, y, z; }; + b32 E[3]; +} bv3; + +typedef union { + struct { f32 x, y; }; + struct { f32 w, h; }; + f32 E[2]; +} v2; +#define V2_INFINITY (v2){{-inf32(), inf32()}} + +typedef union { + struct { f32 x, y, z; }; + struct { f32 w, h, d; }; + struct { v2 xy; f32 _1; }; + struct { f32 _2; v2 yz; }; + f32 E[3]; +} v3; + +typedef union { + struct { f32 x, y, z, w; }; + struct { f32 r, g, b, a; }; + struct { v3 xyz; f32 _1; }; + struct { f32 _2; v3 yzw; }; + struct { v2 xy, zw; }; + f32 E[4]; +} v4; + +typedef union { + struct { v4 x, y, z, w; }; + v4 c[4]; + f32 E[16]; +} m4; + +typedef enum { + ArenaFlag_NoChain = 1 << 0, + + ArenaFlag_CreationMask = ArenaFlag_NoChain, + + ArenaFlag_Sealed = 1 << 31, +} ArenaFlags; + +typedef struct { + u64 reserve_size; + u64 commit_size; + ArenaFlags flags; + + void *optional_backing_store; + + char *name; + char *allocation_site_file; + i32 allocation_site_line; +} ArenaParameters; + +typedef struct Arena Arena; +struct Arena { + u64 position; + u64 committed; + u64 reserved; + + // NOTE(rnp): arena chain + u64 base_position; // position relative to first arena in chain + Arena *prev; + Arena *current; + + u64 reserve_size; + u64 commit_size; + ArenaFlags flags; + + char *name; + char *allocation_site_file; + i32 allocation_site_line; +}; +typedef struct { Arena *arena; u64 position; } Temp; + +typedef struct { i64 length; u8 *data; } str8; +#define str8(s) (str8){.length = countof(s) - 1, .data = (u8 *)s} +#define str8_comp(s) {sizeof(s) - 1, (u8 *)s} +#define str8_struct(v) (str8){.length = sizeof(*v), .data = (u8 *)v} + +typedef struct { i64 length; u16 *data; } str16; + +#endif /* BASE_TYPES_H */ diff --git a/base_win32.c b/base_win32.c @@ -0,0 +1,321 @@ +/* See LICENSE for license details. */ + +/* NOTE(rnp): provides the platform layer for everything in this repo. */ + +#define OS_SHARED_MEMORY_NAME "Local\\ogl_beamformer_parameters" + +#define OS_PATH_SEPARATOR_CHAR '\\' +#define OS_PATH_SEPARATOR "\\" + +#include "base_platform.h" + +#include "util.h" + +#define STD_INPUT_HANDLE -10 +#define STD_OUTPUT_HANDLE -11 +#define STD_ERROR_HANDLE -12 + +#define PAGE_READONLY 0x02 +#define PAGE_READWRITE 0x04 +#define MEM_COMMIT 0x1000 +#define MEM_RESERVE 0x2000 +#define MEM_DECOMMIT 0x4000 +#define MEM_RELEASE 0x8000 + +#define GENERIC_WRITE 0x40000000 +#define GENERIC_READ 0x80000000 + +#define FILE_SHARE_READ 0x00000001 +#define FILE_MAP_ALL_ACCESS 0x000F001F +#define FILE_FLAG_BACKUP_SEMANTICS 0x02000000 +#define FILE_FLAG_OVERLAPPED 0x40000000 + +#define FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010 + +#define FILE_ACTION_MODIFIED 0x00000003 + +#define CREATE_ALWAYS 2 +#define OPEN_EXISTING 3 + +#define THREAD_SET_LIMITED_INFORMATION 0x0400 + +/* NOTE: this is packed because the w32 api designers are dumb and ordered the members + * incorrectly. They worked around it be making the ft* members a struct {u32, u32} which + * is aligned on a 4-byte boundary. Then in their documentation they explicitly tell you not + * to cast to u64 because "it can cause alignment faults on 64-bit Windows" - go figure */ +typedef struct w32_file_info w32_file_info; +pack_struct(struct w32_file_info { + u32 dwFileAttributes; + u64 ftCreationTime; + u64 ftLastAccessTime; + u64 ftLastWriteTime; + u32 dwVolumeSerialNumber; + u32 nFileSizeHigh; + u32 nFileSizeLow; + u32 nNumberOfLinks; + u32 nFileIndexHigh; + u32 nFileIndexLow; +}); + +typedef struct { + u32 next_entry_offset; + u32 action; + u32 filename_size; + u16 filename[]; +} w32_file_notify_info; + +typedef struct { + u16 architecture; + u16 _pad1; + u32 page_size; + i64 minimum_application_address; + i64 maximum_application_address; + u64 active_processor_mask; + u32 number_of_processors; + u32 processor_type; + u32 allocation_granularity; + u16 processor_level; + u16 processor_revision; +} w32_system_info; + +typedef struct { + uptr internal, internal_high; + union { + struct {u32 off, off_high;}; + iptr pointer; + }; + iptr event_handle; +} w32_overlapped; + +typedef enum { + W32IOEvent_FileWatch, +} W32IOEvent; + +typedef struct { + u64 tag; + iptr context; +} w32_io_completion_event; + +typedef struct { + iptr *semaphores; + u32 reserved_count; +} w32_shared_memory_context; + +#define W32(r) __declspec(dllimport) r __stdcall +W32(b32) CloseHandle(iptr); +W32(b32) CopyFileA(c8 *, c8 *, b32); +W32(iptr) CreateFileA(c8 *, u32, u32, void *, u32, u32, void *); +W32(iptr) CreateFileMappingA(iptr, void *, u32, u32, u32, c8 *); +W32(iptr) CreateIoCompletionPort(iptr, iptr, uptr, u32); +W32(iptr) CreateSemaphoreA(iptr, i32, i32, c8 *); +W32(b32) DeleteFileA(c8 *); +W32(void) ExitProcess(i32); +W32(i32) GetFileAttributesA(c8 *); +W32(b32) GetFileInformationByHandle(iptr, void *); +W32(i32) GetLastError(void); +W32(b32) GetQueuedCompletionStatus(iptr, u32 *, uptr *, w32_overlapped **, u32); +W32(iptr) GetStdHandle(i32); +W32(void) GetSystemInfo(w32_system_info *); +W32(void *) MapViewOfFile(iptr, u32, u32, u32, u64); +W32(b32) QueryPerformanceCounter(u64 *); +W32(b32) QueryPerformanceFrequency(u64 *); +W32(b32) ReadDirectoryChangesW(iptr, u8 *, u32, b32, u32, u32 *, void *, void *); +W32(b32) ReadFile(iptr, u8 *, i32, i32 *, void *); +W32(b32) ReleaseSemaphore(iptr, i32, i32 *); +W32(u32) WaitForSingleObject(iptr, u32); +W32(b32) WaitOnAddress(void *, void *, u64, u32); +W32(i32) WakeByAddressAll(void *); +W32(b32) WriteFile(iptr, u8 *, i32, i32 *, void *); +W32(void *) VirtualAlloc(u8 *, i64, u32, u32); +W32(b32) VirtualFree(void *, u64, u32); +W32(b32) VirtualProtect(void *, u64, u32, u32 *); + +global OSSystemInfo win32_system_info; + +function b32 +os_write_file(iptr file, void *data, i64 length) +{ + i32 wlen = 0; + if (length > 0 && length <= (i64)U32_MAX) WriteFile(file, data, (i32)length, &wlen, 0); + return length == wlen; +} + +BASE_EXPORT no_return void +os_exit(i32 code) +{ + ExitProcess(code); + unreachable(); +} + +function u64 +os_timer_frequency(void) +{ + u64 result; + QueryPerformanceFrequency(&result); + return result; +} + +BASE_EXPORT u64 +os_timer_count(void) +{ + u64 result; + QueryPerformanceCounter(&result); + return result; +} + +function void +os_system_info_init(void) +{ + w32_system_info info = {0}; + GetSystemInfo(&info); + + win32_system_info.timer_frequency = os_timer_frequency(); + win32_system_info.logical_processor_count = info.number_of_processors; + win32_system_info.page_size = info.page_size; + win32_system_info.path_separator_byte = '\\'; +} + +BASE_EXPORT OSSystemInfo * +os_system_info(void) +{ + #if BASE_PLATFORM_NO_MAIN + if unlikely(win32_system_info.path_separator_byte == 0) + os_system_info_init(); + #endif + return &win32_system_info; +} + +BASE_EXPORT void * +os_memory_reserve(u64 size) +{ + void *result = VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE); + return result; +} + +BASE_EXPORT void +os_memory_release(void *base, u64 size) +{ + // NOTE(rnp): size must be 0 on w32, no partial releasing + VirtualFree(base, 0, MEM_RELEASE); +} + +BASE_EXPORT b32 +os_memory_commit(void *base, u64 size) +{ + b32 result = VirtualAlloc(base, size, MEM_COMMIT, PAGE_READWRITE) != 0; + return result; +} + +BASE_EXPORT void +os_memory_uncommit(void *base, u64 size) +{ + VirtualFree(base, size, MEM_DECOMMIT); +} + +BASE_EXPORT void +os_memory_seal(void *base, u64 size) +{ + u32 w32_dummy; + VirtualProtect(base, size, PAGE_READONLY, &w32_dummy); +} + +BASE_EXPORT OSW32Semaphore +os_w32_create_semaphore(const char *name, i32 initial_count, i32 maximum_count) +{ + OSW32Semaphore result = {(u64)CreateSemaphoreA(0, initial_count, maximum_count, (c8 *)name)}; + return result; +} + +BASE_EXPORT u32 +os_w32_semaphore_wait(OSW32Semaphore handle, u32 timeout_ms) +{ + b32 result = !WaitForSingleObject(handle.value[0], timeout_ms); + return result; +} + +BASE_EXPORT void +os_w32_semaphore_release(OSW32Semaphore handle, i32 count) +{ + ReleaseSemaphore(handle.value[0], count, 0); +} + +BASE_EXPORT str8 +os_read_entire_file(Arena *arena, const char *file) +{ + str8 result = {0}; + w32_file_info fileinfo; + iptr h = CreateFileA((c8 *)file, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0); + if (h >= 0 && GetFileInformationByHandle(h, &fileinfo)) { + i64 filesize = (i64)fileinfo.nFileSizeHigh << 32; + filesize |= (i64)fileinfo.nFileSizeLow; + result.data = push_array(arena, u8, filesize); + result.length = filesize; + i32 rlen; + if (!ReadFile(h, result.data, (i32)filesize, &rlen, 0) || rlen != filesize) { + arena_pop(arena, filesize); + zero_struct(&result); + } + } + if (h >= 0) CloseHandle(h); + + return result; +} + +function b32 +os_write_new_file(char *fname, str8 raw) +{ + b32 result = 0; + iptr h = CreateFileA(fname, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0); + if (h >= 0) { + while (raw.length > 0) { + i64 length = Min(raw.length, (i64)GB(2)); + result = os_write_file(h, raw.data, length); + if (!result) break; + raw = str8_cut_head(raw, length); + } + CloseHandle(h); + } + return result; +} + +function b32 +os_file_exists(char *path) +{ + b32 result = GetFileAttributesA(path) != -1; + return result; +} + +function b32 +os_copy_file(char *name, char *new) +{ + return CopyFileA(name, new, 0); +} + +BASE_EXPORT b32 +os_wait_on_address(i32 *value, i32 current, u32 timeout_ms) +{ + return WaitOnAddress(value, &current, sizeof(*value), timeout_ms); +} + +BASE_EXPORT void +os_wake_all_waiters(i32 *sync) +{ + if (sync) { + atomic_store_u32(sync, 0); + WakeByAddressAll(sync); + } +} + +#if !BASE_PLATFORM_NO_MAIN +BASE_IMPORT void entry_point(i32 argc, char *argv[]); + +extern i32 +main(i32 argc, char *argv[]) +{ + os_system_info_init(); + + entry_point(argc, argv); + + return 0; +} +#endif diff --git a/beamformer.h b/beamformer.h @@ -4,7 +4,7 @@ #include <stdint.h> -#define BEAMFORMER_NAME_STRING "OGL Beamformer" +#define BEAMFORMER_NAME_STRING "VK Beamformer" /////////////////////////////// // COMPILE TIME CONFIGURATION @@ -64,50 +64,17 @@ /////////////////// // REQUIRED OS API -#define OSInvalidHandleValue ((u64)-1) -typedef struct { uint64_t value[1]; } OSBarrier; -typedef struct { uint64_t value[1]; } OSHandle; -typedef struct { uint64_t value[1]; } OSLibrary; -typedef struct { uint64_t value[1]; } OSThread; -typedef struct { uint64_t value[1]; } OSWindow; -typedef struct { uint64_t value[1]; } OSW32Semaphore; - -typedef uint64_t os_thread_entry_point_fn(void *user_context); - -typedef struct { - uint64_t timer_frequency; - - uint32_t logical_processor_count; - uint32_t page_size; - - uint8_t path_separator_byte; -} OSSystemInfo; - -BEAMFORMER_IMPORT OSSystemInfo * os_system_info(void); - -BEAMFORMER_IMPORT void * os_memory_reserve(uint64_t size); -BEAMFORMER_IMPORT void os_memory_release(void *base, uint64_t size); -BEAMFORMER_IMPORT uint32_t os_memory_commit(void *base, uint64_t size); -BEAMFORMER_IMPORT void os_memory_uncommit(void *base, uint64_t size); -BEAMFORMER_IMPORT void os_memory_seal(void *base, uint64_t size); - -BEAMFORMER_IMPORT OSThread os_create_thread(const char *name, void *user_context, os_thread_entry_point_fn *fn); -BEAMFORMER_IMPORT OSBarrier os_barrier_alloc(uint32_t thread_count); -BEAMFORMER_IMPORT void os_barrier_enter(OSBarrier); - -/* NOTE(rnp): since the beamformer may spawn threads, which may need to keep time, - * passing in a single timer value with the rest of the input is insufficient. */ -BEAMFORMER_IMPORT uint64_t os_timer_count(void); +// +// NOTE(rnp): in addition to the platform layer defined in base_platform.h +// the beamformer additionally requires the following functions. BEAMFORMER_IMPORT void os_add_file_watch(const char *path, int64_t path_length, void *user_context); -BEAMFORMER_IMPORT int64_t os_read_entire_file(const char *file, void *buffer, int64_t buffer_capacity); BEAMFORMER_IMPORT void * os_lookup_symbol(OSLibrary library, const char *symbol); -/* NOTE(rnp): memory watch timed waiting functions. (-1) is an infinite timeout. the beamformer - * will use these with the intention of yielding the thread back to the OS. */ -BEAMFORMER_IMPORT uint32_t os_wait_on_address(int32_t *lock, int32_t current, uint32_t timeout_ms); -BEAMFORMER_IMPORT void os_wake_all_waiters(int32_t *lock); +BEAMFORMER_IMPORT OSThread os_create_thread(const char *name, void *user_context, os_thread_entry_point_fn *fn); +BEAMFORMER_IMPORT OSBarrier os_barrier_alloc(u32 thread_count); +BEAMFORMER_IMPORT void os_barrier_enter(OSBarrier); // NOTE(rnp): currently beamformer will only create one window. // once raylib is removed it may request multiple @@ -122,19 +89,9 @@ BEAMFORMER_IMPORT void os_set_clipboard_text(uint8_t *data, int64_t le BEAMFORMER_IMPORT void os_console_log(uint8_t *data, int64_t length); BEAMFORMER_IMPORT void os_fatal(uint8_t *data, int64_t length); - // NOTE(rnp): for vulkan cross API export on win32 (will be removed eventually) BEAMFORMER_IMPORT void os_release_handle(OSHandle handle); -/* NOTE(rnp): this functionality is only needed on win32 to provide cross process - * synchronization. While posix has equivalent functionality there is no reason to - * use it over a value located in shared memory. */ -#if defined(_WIN32) -BEAMFORMER_IMPORT OSW32Semaphore os_w32_create_semaphore(const char *name, int32_t initial_count, int32_t maximum_count); -BEAMFORMER_IMPORT uint32_t os_w32_semaphore_wait(OSW32Semaphore, uint32_t timeout_ms); -BEAMFORMER_IMPORT void os_w32_semaphore_release(OSW32Semaphore, int32_t count); -#endif - ////////////////////////////// // BEAMFORMER APPLICATION API diff --git a/beamformer_core.c b/beamformer_core.c @@ -21,7 +21,7 @@ * [ ]: bug: reinit cuda on hot-reload */ -#include "compiler.h" +#include "base_platform.h" #if defined(BEAMFORMER_DEBUG) && !defined(BEAMFORMER_EXPORT) && OS_WINDOWS #define BEAMFORMER_EXPORT __declspec(dllexport) @@ -936,16 +936,19 @@ beamformer_reload_pipeline(VulkanHandle *pipeline, BeamformerShaderReloadInfo *s stream_append_shader_header(&shader_stream, reloadable_index, sris[i].shader_descriptor, sris[i].layout); + str8 shader_text; if (BakeShaders) { stream_append_str8(&shader_stream, sris[i].filename_or_data); + shader_text = arena_stream_commit_zero(scratch, &shader_stream); } else { - shader_stream.widx += os_read_entire_file((c8 *)paths[i].data, - shader_stream.data + shader_stream.widx, - shader_stream.cap - shader_stream.widx); + str8 stream_data = arena_stream_commit(scratch, &shader_stream); + str8 shader_data = os_read_entire_file(scratch, (c8 *)paths[i].data); + // NOTE(rnp): kinda sucky but need to make sure these are a contiguous string + shader_text = push_str8_from_parts(scratch, str8(""), stream_data, shader_data); } infos[i].kind = sris[i].shader_kind; - infos[i].text = arena_stream_commit_zero(scratch, &shader_stream); + infos[i].text = shader_text; infos[i].name = beamformer_shader_names[sris[i].shader]; infos[i].specialization_data = sris[i].shader_descriptor ? &sris[i].shader_descriptor->bake : 0; infos[i].specialization_struct_id = beamformer_base_shader_to_bake_struct_id[reloadable_index]; diff --git a/build.c b/build.c @@ -9,8 +9,21 @@ */ #define BEAMFORMER_IMPORT function +#define BASE_EXPORT function +#define BASE_IMPORT function + #include "util.h" +#define BUILD_DEPS_LIST \ + X("base_compiler.h") \ + X("base_linux.c") \ + X("base_platform.h") \ + X("base_types.h") \ + X("base_win32.c") \ + X("meta.h") \ + X("util.c") \ + X("util.h") \ + #include <stdarg.h> #include <setjmp.h> #include <stdio.h> @@ -59,7 +72,7 @@ global char *g_argv0; #include <sys/select.h> #include <sys/wait.h> - #include "os_linux.c" + #include "base_linux.c" #define W32_DECL(x) @@ -72,7 +85,7 @@ global char *g_argv0; #include <string.h> - #include "os_win32.c" + #include "base_win32.c" #define W32_DECL(x) x @@ -545,12 +558,6 @@ os_wait_close_process(iptr handle) #endif -function str8 -read_entire_file(const char *file, Arena *arena) -{ - return os_read_file_into_arena(file, arena); -} - #define needs_rebuild(b, ...) needs_rebuild_(b, ((char *[]){__VA_ARGS__}), \ (sizeof((char *[]){__VA_ARGS__}) / sizeof(char *))) function b32 @@ -617,7 +624,10 @@ function void check_rebuild_self(Arena *arena, i32 argc, char *argv[]) { char *binary = shift(argv, argc); - if (needs_rebuild(binary, __FILE__, "meta.h", "os_win32.c", "os_linux.c", "util.c", "util.h")) { + #define X(file, ...) file, + char *deps[] = {__FILE__, BUILD_DEPS_LIST}; + #undef X + if (needs_rebuild_(binary, deps, countof(deps))) { Stream name_buffer = arena_stream(arena); stream_append_str8s(&name_buffer, str8_from_c_str(binary), str8(".old")); char *old_name = (char *)arena_stream_commit_zero(arena, &name_buffer).data; @@ -1561,7 +1571,7 @@ meta_entry_extract_scope(MetaEntry *base, i64 entry_count) function MetaEntryStack meta_entry_stack_from_file(Arena *arena, char *file) { - MetaParser parser = {.p.s = read_entire_file(file, arena)}; + MetaParser parser = {.p.s = os_read_entire_file(arena, file)}; MetaEntryStack result = {.raw = parser.p.s}; compiler_file = file; @@ -3216,7 +3226,7 @@ metagen_run_emit(MetaprogramContext *m, MetaContext *ctx, MetaEmitOperationList DeferLoop(scratch = temp_begin(m->scratch), temp_end(scratch)) { str8 filename = push_str8_from_parts(m->scratch, str8(OS_PATH_SEPARATOR), ctx->directory, op->string); - str8 file = read_entire_file((c8 *)filename.data, m->scratch); + str8 file = os_read_entire_file(m->scratch, (c8 *)filename.data); m->indentation_level++; metagen_push_byte_array(m, file); m->indentation_level--; @@ -3779,7 +3789,7 @@ meta_push_shader_bake(MetaprogramContext *m, MetaContext *ctx) DeferLoop(scratch = temp_begin(m->scratch), temp_end(scratch)) { str8 filename = push_str8_from_parts(m->scratch, str8(OS_PATH_SEPARATOR), str8("shaders"), s->files[it]); - str8 file = read_entire_file((c8 *)filename.data, m->scratch); + str8 file = os_read_entire_file(m->scratch, (c8 *)filename.data); metagen_push_byte_array(m, file); } } @@ -4680,7 +4690,7 @@ metagen_emit_helper_library_header(MetaContext *ctx, Arena *arena) build_log_generate("Library Header"); - str8 base_header = read_entire_file("lib/ogl_beamformer_lib_base.h", arena); + str8 base_header = os_read_entire_file(arena, "lib/ogl_beamformer_lib_base.h"); MetaprogramContext m[1] = {{.stream = arena_stream(arena), .scratch = ctx->scratch}}; @@ -5154,8 +5164,8 @@ metagen_file_direct(Arena *arena, char *filename) return result; } -i32 -main(i32 argc, char *argv[]) +BASE_IMPORT void +entry_point(i32 argc, char *argv[]) { u64 start_time = os_timer_count(); g_argv0 = argv[0]; @@ -5175,7 +5185,7 @@ main(i32 argc, char *argv[]) arena_clear(arena); MetaContext *meta = metagen_load_context(arena, "beamformer.meta"); - if (!meta) return 1; + if (!meta) os_exit(1); Temp scratch; DeferLoop(scratch = temp_begin(arena), temp_end(scratch)) @@ -5189,8 +5199,8 @@ main(i32 argc, char *argv[]) parse_config(argc, argv); - if (!build_raylib(arena)) return 1; - if (!build_glslang(arena)) return 1; + if (!build_raylib(arena)) os_exit(1); + if (!build_glslang(arena)) os_exit(1); ///////////////// // lib/tests @@ -5213,5 +5223,5 @@ main(i32 argc, char *argv[]) build_log_info("took %0.03f [s]", seconds); } - return result != 1; + os_exit(result != 1); } diff --git a/external/glslang_local/glslang.cpp b/external/glslang_local/glslang.cpp @@ -1,7 +1,7 @@ /* See LICENSE for license details. */ // NOTE(rnp): an almost single file build for glslang -#include "../../compiler.h" +#include "../../base_compiler.h" #include "SPIRV/SpvBuilder.cpp" diff --git a/intrinsics.c b/intrinsics.c @@ -1,234 +0,0 @@ -/* See LICENSE for license details. */ -#include "compiler.h" - -#if COMPILER_CLANG || COMPILER_GCC - #define force_inline inline __attribute__((always_inline)) -#elif COMPILER_MSVC - #define force_inline __forceinline -#endif - -#if COMPILER_MSVC || (COMPILER_CLANG && OS_WINDOWS) - #pragma section(".rdata$", read) - #define read_only __declspec(allocate(".rdata$")) -#elif COMPILER_CLANG - #define read_only __attribute__((section(".rodata"))) -#elif COMPILER_GCC - /* TODO(rnp): how do we do this with gcc, putting it in rodata causes warnings and writing to - * it doesn't cause a fault */ - #define read_only -#endif - -#if !defined(countof) - #define countof(a) (i64)(sizeof(a) / sizeof(*a)) -#endif - -#if COMPILER_MSVC - #define alignas(n) __declspec(align(n)) - #define pack_struct(s) __pragma(pack(push, 1)) s __pragma(pack(pop)) - #define no_return __declspec(noreturn) - - #define likely(x) (x) - #define unlikely(x) (x) - - #define print_format(f, va) - - #define assume(x) __assume(x) - #define debugbreak() __debugbreak() - #define unreachable() __assume(0) - - #if ARCH_ARM64 - #define cpu_yield() __yield() - #define store_fence() __dmb(0x0A) // 0x0A: ishst - #endif - - #define atomic_add_u32(ptr, n) _InterlockedExchangeAdd((volatile u32 *)(ptr), (n)) - #define atomic_add_u64(ptr, n) _InterlockedExchangeAdd64((volatile u64 *)(ptr), (n)) - #define atomic_and_u32(ptr, n) _InterlockedAnd((volatile u32 *)(ptr), (n)) - #define atomic_and_u64(ptr, n) _InterlockedAnd64((volatile u64 *)(ptr), (n)) - #define atomic_cas_u32(ptr, cptr, n) (_InterlockedCompareExchange((volatile u32 *)(ptr), *(cptr), (n)) == *(cptr)) - #define atomic_cas_u64(ptr, cptr, n) (_InterlockedCompareExchange64((volatile u64 *)(ptr), *(cptr), (n)) == *(cptr)) - #define atomic_load_u32(ptr) *((volatile u32 *)(ptr)) - #define atomic_load_u64(ptr) *((volatile u64 *)(ptr)) - #define atomic_or_u32(ptr, n) _InterlockedOr((volatile u32 *)(ptr), (n)) - #define atomic_store_u32(ptr, n) *((volatile u32 *)(ptr)) = (u32)(n) - #define atomic_store_u64(ptr, n) *((volatile u64 *)(ptr)) = (u64)(n) - #define atomic_swap_u32(ptr, n) _InterlockedExchange((volatile u32 *)(ptr), n) - #define atomic_swap_u64(ptr, n) _InterlockedExchange64((volatile u64 *)(ptr), n) - - #define atan2_f32(y, x) atan2f(y, x) - #define cos_f32(a) cosf(a) - #define sin_f32(a) sinf(a) - #define tan_f32(a) tanf(a) - #define ceil_f32(a) ceilf(a) - #define sqrt_f32(a) sqrtf(a) - - #define exp_f64(a) exp(a) - #define sqrt_f64(a) sqrt(a) - -#else - #define alignas(n) __attribute__((aligned(n))) - #define pack_struct(s) s __attribute__((packed)) - #define no_return __attribute__((noreturn)) - - #define likely(x) (__builtin_expect(!!(x), 1)) - #define unlikely(x) (__builtin_expect(!!(x), 0)) - - #define print_format(f, va) __attribute__((format(printf, f, va))) - - #if COMPILER_CLANG - #define assume(x) __builtin_assume(x) - #else - #if defined(__has_attribute) - #if __has_attribute(assume) - #define assume(x) __attribute__((assume(x))) - #endif - #endif - #endif - #if !defined(assume) - #define assume(x) if (!(x)) unreachable() - #endif - #define unreachable() __builtin_unreachable() - #if ARCH_ARM64 - /* TODO? debuggers just loop here forever and need a manual PC increment (step over) */ - #define debugbreak() asm volatile ("brk 0xf000") - #define cpu_yield() asm volatile ("yield") - #define store_fence() asm volatile ("dmb ishst" ::: "memory") - #else - #define debugbreak() asm volatile ("int3; nop") - #endif - - #define atomic_add_u64(ptr, n) __atomic_fetch_add(ptr, n, __ATOMIC_SEQ_CST) - #define atomic_and_u64(ptr, n) __atomic_and_fetch(ptr, n, __ATOMIC_SEQ_CST) - #define atomic_cas_u64(ptr, cptr, n) __atomic_compare_exchange_n(ptr, cptr, n, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) - #define atomic_load_u64(ptr) __atomic_load_n(ptr, __ATOMIC_SEQ_CST) - #define atomic_or_u32(ptr, n) __atomic_or_fetch(ptr, n, __ATOMIC_SEQ_CST) - #define atomic_store_u64(ptr, n) __atomic_store_n(ptr, n, __ATOMIC_SEQ_CST) - #define atomic_swap_u64(ptr, n) __atomic_exchange_n(ptr, n, __ATOMIC_SEQ_CST) - #define atomic_add_u32 atomic_add_u64 - #define atomic_and_u32 atomic_and_u64 - #define atomic_cas_u32 atomic_cas_u64 - #define atomic_load_u32 atomic_load_u64 - #define atomic_store_u32 atomic_store_u64 - #define atomic_swap_u32 atomic_swap_u64 - - #define atan2_f32(y, x) __builtin_atan2f(y, x) - #define cos_f32(a) __builtin_cosf(a) - #define sin_f32(a) __builtin_sinf(a) - #define tan_f32(a) __builtin_tanf(a) - #define ceil_f32(a) __builtin_ceilf(a) - #define sqrt_f32(a) __builtin_sqrtf(a) - - #define exp_f64(a) __builtin_exp(a) - #define sqrt_f64(a) __builtin_sqrt(a) - - #define popcount_u64(a) (u64)__builtin_popcountll(a) -#endif - -#if COMPILER_MSVC - -function force_inline u64 -clz_u64(u64 a) -{ - u64 result = 64, index; - if (a) { - _BitScanReverse64(&index, a); - result = index; - } - return result; -} - -function force_inline u64 -ctz_u64(u64 a) -{ - u64 result = 64, index; - if (a) { - _BitScanForward64(&index, a); - result = index; - } - return result; -} - -#else /* !COMPILER_MSVC */ - -function force_inline u64 -clz_u64(u32 a) -{ - u64 result = 64; - if (a) result = (u64)__builtin_clzll(a); - return result; -} - -function force_inline u64 -ctz_u64(u64 a) -{ - u64 result = 64; - if (a) result = (u64)__builtin_ctzll(a); - return result; -} - -#endif - -#if ARCH_ARM64 -/* NOTE(rnp): we are only doing a handful of f32x4 operations so we will just use NEON and do - * the macro renaming thing. If you are implementing a serious wide vector operation you should - * use SVE(2) instead. The semantics are different however and the code will be written for an - * arbitrary vector bit width. In that case you will also need x86_64 code for determining - * the supported vector width (ideally at runtime though that may not be possible). - */ -#include <arm_neon.h> -typedef float32x4_t f32x4; -typedef int32x4_t i32x4; -typedef uint32x4_t u32x4; - -#define add_f32x4(a, b) vaddq_f32(a, b) -#define cvt_i32x4_f32x4(a) vcvtq_f32_s32(a) -#define cvt_f32x4_i32x4(a) vcvtq_s32_f32(a) -#define div_f32x4(a, b) vdivq_f32(a, b) -#define dup_f32x4(f) vdupq_n_f32(f) -#define floor_f32x4(a) vrndmq_f32(a) -#define load_f32x4(a) vld1q_f32(a) -#define load_i32x4(a) vld1q_s32(a) -#define max_f32x4(a, b) vmaxq_f32(a, b) -#define min_f32x4(a, b) vminq_f32(a, b) -#define mul_f32x4(a, b) vmulq_f32(a, b) -#define set_f32x4(a, b, c, d) vld1q_f32((f32 []){d, c, b, a}) -#define sqrt_f32x4(a) vsqrtq_f32(a) -#define store_f32x4(o, a) vst1q_f32(o, a) -#define store_i32x4(o, a) vst1q_s32(o, a) -#define sub_f32x4(a, b) vsubq_f32(a, b) - -#elif ARCH_X64 -#include <immintrin.h> -typedef __m128 f32x4; -typedef __m128i i32x4; -typedef __m128i u32x4; - -#define add_f32x4(a, b) _mm_add_ps(a, b) -#define cvt_i32x4_f32x4(a) _mm_cvtepi32_ps(a) -#define cvt_f32x4_i32x4(a) _mm_cvtps_epi32(a) -#define div_f32x4(a, b) _mm_div_ps(a, b) -#define dup_f32x4(f) _mm_set1_ps(f) -#define floor_f32x4(a) _mm_floor_ps(a) -#define load_f32x4(a) _mm_loadu_ps(a) -#define load_i32x4(a) _mm_loadu_si128((i32x4 *)a) -#define max_f32x4(a, b) _mm_max_ps(a, b) -#define min_f32x4(a, b) _mm_min_ps(a, b) -#define mul_f32x4(a, b) _mm_mul_ps(a, b) -#define set_f32x4(a, b, c, d) _mm_set_ps(a, b, c, d) -#define sqrt_f32x4(a) _mm_sqrt_ps(a) -#define store_f32x4(o, a) _mm_storeu_ps(o, a) -#define store_i32x4(o, a) _mm_storeu_si128((i32x4 *)o, a) -#define sub_f32x4(a, b) _mm_sub_ps(a, b) - -#define cpu_yield _mm_pause -#define store_fence _mm_sfence - -#endif - -function force_inline f32 -inf32(void) -{ - union {u32 u; f32 f;} result; - result.u = 0x7F800000u; - return result.f; -} diff --git a/lib/ogl_beamformer_lib.c b/lib/ogl_beamformer_lib.c @@ -1,17 +1,20 @@ /* See LICENSE for license details. */ -#include "../compiler.h" - #define BEAMFORMER_IMPORT static +#ifndef BASE_PLATFORM_H +#define BASE_PLATFORM_NO_MAIN 1 +#define BASE_EXPORT static +#endif + #include "../util.h" #include "../generated/beamformer.c" #include "ogl_beamformer_lib_base.h" #if OS_LINUX -#include "../os_linux.c" +#include "../base_linux.c" #elif OS_WINDOWS -#include "../os_win32.c" +#include "../base_win32.c" W32(iptr) OpenFileMappingA(u32, b32, c8 *); diff --git a/main_linux.c b/main_linux.c @@ -1,17 +1,20 @@ /* See LICENSE for license details. */ -#include "compiler.h" - -#if !OS_LINUX -#error This file is only meant to be compiled for Linux -#endif - #ifndef BEAMFORMER_DEBUG #define BEAMFORMER_IMPORT static #define BEAMFORMER_EXPORT static + #define BASE_EXPORT static +#endif + +#define BASE_IMPORT static + +#include "base_platform.h" + +#if !OS_LINUX +#error This file is only meant to be compiled for Linux #endif #include "beamformer.c" -#include "os_linux.c" +#include "base_linux.c" #define OS_SHARED_MEMORY_SIZE GB(2) @@ -86,8 +89,6 @@ typedef struct { BeamformerInput *input; - OSSystemInfo system_info; - struct { OSLinuxFileWatchDirectory *first; OSLinuxFileWatchDirectory *last; @@ -117,12 +118,6 @@ os_entity_allocate(OSLinuxEntityKind kind) return result; } -BEAMFORMER_IMPORT OSSystemInfo * -os_system_info(void) -{ - return &os_linux_context.system_info; -} - BEAMFORMER_IMPORT OSThread os_create_thread(const char *name, void *user_context, os_thread_entry_point_fn *fn) { @@ -176,6 +171,13 @@ os_fatal(u8 *data, i64 length) unreachable(); } +BEAMFORMER_IMPORT void +os_release_handle(OSHandle h) +{ + if ValidHandle(h) + close(h.value[0]); +} + BEAMFORMER_IMPORT void * os_lookup_symbol(OSLibrary library, const char *symbol) { @@ -187,7 +189,7 @@ os_lookup_symbol(OSLibrary library, const char *symbol) function void * allocate_shared_memory(char *name, i64 requested_capacity, u64 *capacity) { - u64 rounded_capacity = round_up_to(requested_capacity, ARCH_X64? KB(4) : os_linux_context.system_info.page_size); + u64 rounded_capacity = round_up_to(requested_capacity, ARCH_X64? KB(4) : linux_system_info.page_size); void *result = 0; i32 fd = shm_open(name, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR); if (fd > 0 && ftruncate(fd, rounded_capacity) != -1) { @@ -431,15 +433,10 @@ dispatch_file_watch_events(void *beamformer, BeamformerInput *input) } } -extern i32 -main(void) +BASE_IMPORT void +entry_point(i32 argc, char *argv[]) { - os_linux_context.system_info.timer_frequency = os_timer_frequency(); - os_linux_context.system_info.logical_processor_count = os_number_of_processors(); - os_linux_context.system_info.page_size = ARCH_X64? KB(4) : getauxval(AT_PAGESZ); - os_linux_context.system_info.path_separator_byte = '/'; - - os_linux_context.arena = arena_create(.name = "Platform Arena"); + os_linux_context.arena = arena_create(.name = "Platform Arena"); os_linux_context.inotify_handle = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); BeamformerInput *input = push_struct(os_linux_context.arena, BeamformerInput); diff --git a/main_w32.c b/main_w32.c @@ -1,5 +1,16 @@ /* See LICENSE for license details. */ -#include "compiler.h" +#ifdef BEAMFORMER_DEBUG + #define BEAMFORMER_IMPORT __declspec(dllexport) + #define BASE_EXPORT __declspec(dllexport) +#else + #define BEAMFORMER_IMPORT static + #define BEAMFORMER_EXPORT static + #define BASE_EXPORT static +#endif + +#define BASE_IMPORT static + +#include "base_platform.h" // NOTE(rnp): for test compilations on linux (we don't use headers from windows) */ #if OS_LINUX @@ -18,15 +29,8 @@ #error This file is only meant to be compiled for Win32 #endif -#ifdef BEAMFORMER_DEBUG - #define BEAMFORMER_IMPORT __declspec(dllexport) -#else - #define BEAMFORMER_IMPORT static - #define BEAMFORMER_EXPORT static -#endif - #include "beamformer.c" -#include "os_win32.c" +#include "base_win32.c" typedef struct { u32 reserved1; @@ -133,8 +137,6 @@ typedef struct { } windows; OSW32Entity *entity_freelist; - - OSSystemInfo system_info; } OSW32_Context; global OSW32_Context os_w32_context; @@ -153,12 +155,6 @@ os_entity_allocate(OSW32EntityKind kind) return result; } -BEAMFORMER_IMPORT OSSystemInfo * -os_system_info(void) -{ - return &os_w32_context.system_info; -} - BEAMFORMER_IMPORT OSThread os_create_thread(const char *name, void *user_context, os_thread_entry_point_fn *fn) { @@ -210,6 +206,13 @@ os_fatal(u8 *data, i64 length) unreachable(); } +BEAMFORMER_IMPORT void +os_release_handle(OSHandle h) +{ + if ValidHandle(h) + CloseHandle(h.value[0]); +} + BEAMFORMER_IMPORT void * os_lookup_symbol(OSLibrary library, const char *symbol) { @@ -221,7 +224,7 @@ os_lookup_symbol(OSLibrary library, const char *symbol) function void * allocate_shared_memory(char *name, i64 requested_capacity, u64 *capacity) { - u64 rounded_capacity = round_up_to(requested_capacity, os_w32_context.system_info.page_size); + u64 rounded_capacity = round_up_to(requested_capacity, win32_system_info.page_size); void *result = 0; iptr h = CreateFileMappingA(-1, 0, PAGE_READWRITE, (rounded_capacity >> 32u), (rounded_capacity & 0xFFFFFFFFul), name); @@ -506,22 +509,12 @@ clear_io_queue(void *beamformer, BeamformerInput *input, Arena *scratch) } } -extern i32 -main(void) +BASE_IMPORT void +entry_point(i32 argc, char *argv[]) { - os_w32_context.error_handle = GetStdHandle(STD_ERROR_HANDLE); - os_w32_context.io_completion_handle = CreateIoCompletionPort(INVALID_FILE, 0, 0, 0); - os_w32_context.system_info.timer_frequency = os_timer_frequency(); - os_w32_context.system_info.path_separator_byte = '\\'; - { - w32_system_info info = {0}; - GetSystemInfo(&info); - - os_w32_context.system_info.page_size = info.page_size; - os_w32_context.system_info.logical_processor_count = info.number_of_processors; - } - - os_w32_context.arena = arena_create(.name = "Platform Arena"); + os_w32_context.arena = arena_create(.name = "Platform Arena"); + os_w32_context.error_handle = GetStdHandle(STD_ERROR_HANDLE); + os_w32_context.io_completion_handle = CreateIoCompletionPort(INVALID_FILE, 0, 0, 0); BeamformerInput *input = push_struct(os_w32_context.arena, BeamformerInput); os_w32_context.input = input; diff --git a/os_linux.c b/os_linux.c @@ -1,216 +0,0 @@ -/* See LICENSE for license details. */ - -/* NOTE(rnp): provides the platform layer for the beamformer. This code must - * be provided by any platform the beamformer is ported to. */ - -#define OS_SHARED_MEMORY_NAME "/ogl_beamformer_shared_memory" - -#define OS_PATH_SEPARATOR_CHAR '/' -#define OS_PATH_SEPARATOR "/" - -#include "util.h" - -#include <errno.h> -#include <fcntl.h> -#include <linux/futex.h> -#include <poll.h> -#include <pthread.h> -#include <sys/auxv.h> -#include <sys/inotify.h> -#include <sys/mman.h> -#include <sys/stat.h> -#include <sys/syscall.h> -#include <sys/sysinfo.h> -#include <unistd.h> - -function b32 -os_write_file(i32 file, void *data, i64 length) -{ - i64 offset = 0; - while (offset < length) { - i64 r = write(file, (u8 *)data + offset, (u64)(length - offset)); - if (r < 0 && errno != EINTR) break; - if (r >= 0) offset += r; - } - return offset == length; -} - -function no_return void -os_exit(i32 code) -{ - _exit(code); - unreachable(); -} - -function u64 -os_timer_frequency(void) -{ - return 1000000000ULL; -} - -BEAMFORMER_IMPORT u64 -os_timer_count(void) -{ - struct timespec time = {0}; - clock_gettime(CLOCK_MONOTONIC, &time); - u64 result = (u64)time.tv_sec * 1000000000ULL + (u64)time.tv_nsec; - return result; -} - -function u64 -os_number_of_processors(void) -{ - u64 set[128 / sizeof(u64)] = {0}; - syscall(SYS_sched_getaffinity, 0, sizeof(set), set); - - u64 result = 0; - for EachElement(set, it) - result += popcount_u64(set[it]); - return result > 0 ? result : 1; -} - -#ifndef BEAMFORMER_H -// TODO(rnp): fix main and platform code split - -function OSSystemInfo * -os_system_info(void) -{ - local_persist b32 ready = 0; - local_persist OSSystemInfo linux_system_info = {0}; - if unlikely(!ready) { - linux_system_info.timer_frequency = os_timer_frequency(); - linux_system_info.logical_processor_count = os_number_of_processors(); - linux_system_info.page_size = ARCH_X64? KB(4) : getauxval(AT_PAGESZ); - linux_system_info.path_separator_byte = '/'; - ready = 1; - } - return &linux_system_info; -} - -#endif - -BEAMFORMER_IMPORT void * -os_memory_reserve(u64 size) -{ - void *result = mmap(0, size, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); - if (result == MAP_FAILED) - result = 0; - return result; -} - -BEAMFORMER_IMPORT void -os_memory_release(void *base, u64 size) -{ - munmap(base, size); -} - -BEAMFORMER_IMPORT b32 -os_memory_commit(void *base, u64 size) -{ - mprotect(base, size, PROT_READ|PROT_WRITE); - return 1; -} - -BEAMFORMER_IMPORT void -os_memory_uncommit(void *base, u64 size) -{ - madvise(base, size, MADV_DONTNEED); - mprotect(base, size, PROT_NONE); -} - -BEAMFORMER_IMPORT void -os_memory_seal(void *base, u64 size) -{ - mprotect(base, size, PROT_READ); -} - -BEAMFORMER_IMPORT OS_READ_ENTIRE_FILE_FN(os_read_entire_file) -{ - i64 result = 0; - struct stat sb; - i32 fd = open(file, O_RDONLY); - if (fd >= 0 && fstat(fd, &sb) >= 0) { - if (buffer_capacity >= sb.st_size) { - do { - i64 rlen = read(fd, (u8 *)buffer + result, (u64)(sb.st_size - result)); - if (rlen > 0) result += rlen; - } while (result != sb.st_size && errno != EINTR); - if (result != sb.st_size) result = 0; - } - } - if (fd >= 0) close(fd); - - return result; -} - -function OS_WRITE_NEW_FILE_FN(os_write_new_file) -{ - b32 result = 0; - i32 fd = open(fname, O_WRONLY|O_TRUNC|O_CREAT, 0600); - if (fd != INVALID_FILE) { - result = os_write_file(fd, raw.data, raw.length); - close(fd); - } - return result; -} - -function b32 -os_file_exists(char *path) -{ - struct stat st; - b32 result = stat(path, &st) == 0; - return result; -} - -/* NOTE: complete garbage because there is no standarized copyfile() in POSix */ -function b32 -os_copy_file(char *name, char *new) -{ - b32 result = 0; - struct stat sb; - if (stat(name, &sb) == 0) { - i32 fd_old = open(name, O_RDONLY); - i32 fd_new = open(new, O_WRONLY|O_CREAT, sb.st_mode); - if (fd_old >= 0 && fd_new >= 0) { - u8 buf[4096]; - i64 copied = 0; - while (copied != sb.st_size) { - i64 r = read(fd_old, buf, countof(buf)); - if (r < 0) break; - i64 w = write(fd_new, buf, (u64)r); - if (w < 0) break; - copied += w; - } - result = copied == sb.st_size; - } - if (fd_old != -1) close(fd_old); - if (fd_new != -1) close(fd_new); - } - return result; -} - -BEAMFORMER_IMPORT void -os_release_handle(OSHandle h) -{ - if ValidHandle(h) - close(h.value[0]); -} - -BEAMFORMER_IMPORT OS_WAIT_ON_ADDRESS_FN(os_wait_on_address) -{ - struct timespec *timeout = 0, timeout_value; - if (timeout_ms != (u32)-1) { - timeout_value.tv_sec = timeout_ms / 1000; - timeout_value.tv_nsec = (timeout_ms % 1000) * 1000000; - timeout = &timeout_value; - } - return syscall(SYS_futex, value, FUTEX_WAIT, current, timeout, 0, 0) == 0; -} - -BEAMFORMER_IMPORT OS_WAKE_ALL_WAITERS_FN(os_wake_all_waiters) -{ - if (sync) { - atomic_store_u32(sync, 0); - syscall(SYS_futex, sync, FUTEX_WAKE, I32_MAX, 0, 0, 0); - } -} diff --git a/os_win32.c b/os_win32.c @@ -1,303 +0,0 @@ -/* See LICENSE for license details. */ - -#define OS_SHARED_MEMORY_NAME "Local\\ogl_beamformer_parameters" - -#define OS_PATH_SEPARATOR_CHAR '\\' -#define OS_PATH_SEPARATOR "\\" - -#include "util.h" - -#define STD_INPUT_HANDLE -10 -#define STD_OUTPUT_HANDLE -11 -#define STD_ERROR_HANDLE -12 - -#define PAGE_READONLY 0x02 -#define PAGE_READWRITE 0x04 -#define MEM_COMMIT 0x1000 -#define MEM_RESERVE 0x2000 -#define MEM_DECOMMIT 0x4000 -#define MEM_RELEASE 0x8000 - -#define GENERIC_WRITE 0x40000000 -#define GENERIC_READ 0x80000000 - -#define FILE_SHARE_READ 0x00000001 -#define FILE_MAP_ALL_ACCESS 0x000F001F -#define FILE_FLAG_BACKUP_SEMANTICS 0x02000000 -#define FILE_FLAG_OVERLAPPED 0x40000000 - -#define FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010 - -#define FILE_ACTION_MODIFIED 0x00000003 - -#define CREATE_ALWAYS 2 -#define OPEN_EXISTING 3 - -#define THREAD_SET_LIMITED_INFORMATION 0x0400 - -/* NOTE: this is packed because the w32 api designers are dumb and ordered the members - * incorrectly. They worked around it be making the ft* members a struct {u32, u32} which - * is aligned on a 4-byte boundary. Then in their documentation they explicitly tell you not - * to cast to u64 because "it can cause alignment faults on 64-bit Windows" - go figure */ -typedef struct w32_file_info w32_file_info; -pack_struct(struct w32_file_info { - u32 dwFileAttributes; - u64 ftCreationTime; - u64 ftLastAccessTime; - u64 ftLastWriteTime; - u32 dwVolumeSerialNumber; - u32 nFileSizeHigh; - u32 nFileSizeLow; - u32 nNumberOfLinks; - u32 nFileIndexHigh; - u32 nFileIndexLow; -}); - -typedef struct { - u32 next_entry_offset; - u32 action; - u32 filename_size; - u16 filename[]; -} w32_file_notify_info; - -typedef struct { - u16 architecture; - u16 _pad1; - u32 page_size; - i64 minimum_application_address; - i64 maximum_application_address; - u64 active_processor_mask; - u32 number_of_processors; - u32 processor_type; - u32 allocation_granularity; - u16 processor_level; - u16 processor_revision; -} w32_system_info; - -typedef struct { - uptr internal, internal_high; - union { - struct {u32 off, off_high;}; - iptr pointer; - }; - iptr event_handle; -} w32_overlapped; - -typedef enum { - W32IOEvent_FileWatch, -} W32IOEvent; - -typedef struct { - u64 tag; - iptr context; -} w32_io_completion_event; - -typedef struct { - iptr *semaphores; - u32 reserved_count; -} w32_shared_memory_context; - -#define W32(r) __declspec(dllimport) r __stdcall -W32(b32) CloseHandle(iptr); -W32(b32) CopyFileA(c8 *, c8 *, b32); -W32(iptr) CreateFileA(c8 *, u32, u32, void *, u32, u32, void *); -W32(iptr) CreateFileMappingA(iptr, void *, u32, u32, u32, c8 *); -W32(iptr) CreateIoCompletionPort(iptr, iptr, uptr, u32); -W32(iptr) CreateSemaphoreA(iptr, i32, i32, c8 *); -W32(b32) DeleteFileA(c8 *); -W32(void) ExitProcess(i32); -W32(i32) GetFileAttributesA(c8 *); -W32(b32) GetFileInformationByHandle(iptr, void *); -W32(i32) GetLastError(void); -W32(b32) GetQueuedCompletionStatus(iptr, u32 *, uptr *, w32_overlapped **, u32); -W32(iptr) GetStdHandle(i32); -W32(void) GetSystemInfo(w32_system_info *); -W32(void *) MapViewOfFile(iptr, u32, u32, u32, u64); -W32(b32) QueryPerformanceCounter(u64 *); -W32(b32) QueryPerformanceFrequency(u64 *); -W32(b32) ReadDirectoryChangesW(iptr, u8 *, u32, b32, u32, u32 *, void *, void *); -W32(b32) ReadFile(iptr, u8 *, i32, i32 *, void *); -W32(b32) ReleaseSemaphore(iptr, i32, i32 *); -W32(u32) WaitForSingleObject(iptr, u32); -W32(b32) WaitOnAddress(void *, void *, u64, u32); -W32(i32) WakeByAddressAll(void *); -W32(b32) WriteFile(iptr, u8 *, i32, i32 *, void *); -W32(void *) VirtualAlloc(u8 *, i64, u32, u32); -W32(b32) VirtualFree(void *, u64, u32); -W32(b32) VirtualProtect(void *, u64, u32, u32 *); - -function b32 -os_write_file(iptr file, void *data, i64 length) -{ - i32 wlen = 0; - if (length > 0 && length <= (i64)U32_MAX) WriteFile(file, data, (i32)length, &wlen, 0); - return length == wlen; -} - -function no_return void -os_exit(i32 code) -{ - ExitProcess(1); - unreachable(); -} - -function u64 -os_timer_frequency(void) -{ - u64 result; - QueryPerformanceFrequency(&result); - return result; -} - -BEAMFORMER_IMPORT u64 -os_timer_count(void) -{ - u64 result; - QueryPerformanceCounter(&result); - return result; -} - -#ifndef BEAMFORMER_H -// TODO(rnp): fix main and platform code split - -function OSSystemInfo * -os_system_info(void) -{ - local_persist b32 ready = 0; - local_persist OSSystemInfo w32_platform_info = {0}; - if unlikely(!ready) { - w32_system_info info = {0}; - GetSystemInfo(&info); - w32_platform_info.timer_frequency = os_timer_frequency(); - w32_platform_info.path_separator_byte = '\\'; - w32_platform_info.page_size = info.page_size; - w32_platform_info.logical_processor_count = info.number_of_processors; - ready = 1; - } - return &w32_platform_info; -} - -#endif - -BEAMFORMER_IMPORT void * -os_memory_reserve(u64 size) -{ - void *result = VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE); - return result; -} - -BEAMFORMER_IMPORT void -os_memory_release(void *base, u64 size) -{ - // NOTE(rnp): size must be 0 on w32, no partial releasing - VirtualFree(base, 0, MEM_RELEASE); -} - -BEAMFORMER_IMPORT b32 -os_memory_commit(void *base, u64 size) -{ - b32 result = VirtualAlloc(base, size, MEM_COMMIT, PAGE_READWRITE) != 0; - return result; -} - -BEAMFORMER_IMPORT void -os_memory_uncommit(void *base, u64 size) -{ - VirtualFree(base, size, MEM_DECOMMIT); -} - -BEAMFORMER_IMPORT void -os_memory_seal(void *base, u64 size) -{ - u32 w32_dummy; - VirtualProtect(base, size, PAGE_READONLY, &w32_dummy); -} - -BEAMFORMER_IMPORT OS_READ_ENTIRE_FILE_FN(os_read_entire_file) -{ - i64 result = 0; - w32_file_info fileinfo; - iptr h = CreateFileA((c8 *)file, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0); - if (h >= 0 && GetFileInformationByHandle(h, &fileinfo)) { - i64 filesize = (i64)fileinfo.nFileSizeHigh << 32; - filesize |= (i64)fileinfo.nFileSizeLow; - if (buffer_capacity >= filesize) { - result = filesize; - i32 rlen; - if (!ReadFile(h, buffer, (i32)filesize, &rlen, 0) || rlen != filesize) - result = 0; - } - } - if (h >= 0) CloseHandle(h); - - return result; -} - -function OS_WRITE_NEW_FILE_FN(os_write_new_file) -{ - b32 result = 0; - iptr h = CreateFileA(fname, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0); - if (h >= 0) { - while (raw.length > 0) { - i64 length = Min(raw.length, (i64)GB(2)); - result = os_write_file(h, raw.data, length); - if (!result) break; - raw = str8_cut_head(raw, length); - } - CloseHandle(h); - } - return result; -} - -function b32 -os_file_exists(char *path) -{ - b32 result = GetFileAttributesA(path) != -1; - return result; -} - -function b32 -os_copy_file(char *name, char *new) -{ - return CopyFileA(name, new, 0); -} - -BEAMFORMER_IMPORT void -os_release_handle(OSHandle h) -{ - if ValidHandle(h) - CloseHandle(h.value[0]); -} - -BEAMFORMER_IMPORT OS_WAIT_ON_ADDRESS_FN(os_wait_on_address) -{ - return WaitOnAddress(value, &current, sizeof(*value), timeout_ms); -} - -BEAMFORMER_IMPORT OS_WAKE_ALL_WAITERS_FN(os_wake_all_waiters) -{ - if (sync) { - atomic_store_u32(sync, 0); - WakeByAddressAll(sync); - } -} - -BEAMFORMER_IMPORT OSW32Semaphore -os_w32_create_semaphore(const char *name, i32 initial_count, i32 maximum_count) -{ - OSW32Semaphore result = {(u64)CreateSemaphoreA(0, initial_count, maximum_count, (c8 *)name)}; - return result; -} - -BEAMFORMER_IMPORT u32 -os_w32_semaphore_wait(OSW32Semaphore handle, u32 timeout_ms) -{ - b32 result = !WaitForSingleObject(handle.value[0], timeout_ms); - return result; -} - -BEAMFORMER_IMPORT void -os_w32_semaphore_release(OSW32Semaphore handle, i32 count) -{ - ReleaseSemaphore(handle.value[0], count, 0); -} diff --git a/tests/decode.c b/tests/decode.c @@ -1,5 +1,8 @@ /* See LICENSE for license details. */ -#define LIB_FN function +#define BASE_EXPORT function +#define BASE_IMPORT function +#define BEAMFORMER_LIB_EXPORT function +#include "base_platform.h" #include "ogl_beamformer_lib.c" #include <signal.h> @@ -252,8 +255,8 @@ sigint(i32 _signo) g_should_exit = 1; } -extern i32 -main(i32 argc, char *argv[]) +BASE_IMPORT void +entry_point(i32 argc, char *argv[]) { Options options = parse_argv(argc, argv); @@ -298,6 +301,4 @@ main(i32 argc, char *argv[]) lip.active = 0; beamformer_set_live_parameters(&lip); - - return 0; } diff --git a/tests/throughput.c b/tests/throughput.c @@ -5,7 +5,10 @@ * [ ]: bug: we aren't inserting rf data between each frame */ +#define BASE_EXPORT function +#define BASE_IMPORT function #define BEAMFORMER_LIB_EXPORT function +#include "base_platform.h" #include "ogl_beamformer_lib.c" #include <signal.h> @@ -570,8 +573,8 @@ sigint(i32 _signo) g_should_exit = 1; } -extern i32 -main(i32 argc, char *argv[]) +BASE_IMPORT void +entry_point(i32 argc, char *argv[]) { Options options = parse_argv(argc, argv); @@ -585,6 +588,4 @@ main(i32 argc, char *argv[]) stream_append_str8(&path, str8_from_c_str(options.remaining[0])); execute_study(arena, path, &options); - - return 0; } diff --git a/util.c b/util.c @@ -147,24 +147,6 @@ typedef struct { #define push_struct(a, t) push_array(a, t, 1) #define push_struct_no_zero(a, t) push_array_no_zero(a, t, 1) -#ifndef BEAMFORMER_H -// TODO(rnp): fix main and platform code split -typedef struct { - u64 timer_frequency; - u32 logical_processor_count; - u32 page_size; - u8 path_separator_byte; -} OSSystemInfo; - -BEAMFORMER_IMPORT OSSystemInfo *os_system_info(void); - -BEAMFORMER_IMPORT void *os_memory_reserve(u64 size); -BEAMFORMER_IMPORT void os_memory_release(void *base, u64 size); -BEAMFORMER_IMPORT b32 os_memory_commit(void *base, u64 size); -BEAMFORMER_IMPORT void os_memory_uncommit(void *base, u64 size); -BEAMFORMER_IMPORT void os_memory_seal(void *base, u64 size); -#endif - #define arena_create(...) arena_create_((ArenaParameters){\ .reserve_size = MB(64),\ .commit_size = KB(64),\ diff --git a/util.h b/util.h @@ -2,48 +2,10 @@ #ifndef _UTIL_H_ #define _UTIL_H_ -#include "compiler.h" +#include "base_platform.h" #define da_count i32 -#if COMPILER_MSVC - typedef unsigned __int64 u64; - typedef signed __int64 i64; - typedef unsigned __int32 u32; - typedef signed __int32 i32; - typedef unsigned __int16 u16; - typedef signed __int16 i16; - typedef unsigned __int8 u8; - typedef signed __int8 i8; -#else - typedef __UINT64_TYPE__ u64; - typedef __INT64_TYPE__ i64; - typedef __UINT32_TYPE__ u32; - typedef __INT32_TYPE__ i32; - typedef __UINT16_TYPE__ u16; - typedef __INT16_TYPE__ i16; - typedef __UINT8_TYPE__ u8; - typedef __INT8_TYPE__ i8; -#endif - -typedef char c8; -typedef u8 b8; -typedef u16 b16; -typedef u32 b32; -typedef _Float16 f16; -typedef float f32; -typedef double f64; -typedef i64 iptr; -typedef u64 uptr; - -#ifndef asm -#define asm __asm__ -#endif - -#ifndef typeof -#define typeof __typeof__ -#endif - #if OS_WINDOWS #define EXPORT __declspec(dllexport) #else @@ -85,10 +47,6 @@ typedef u64 uptr; #define arg_list(type, ...) (type []){__VA_ARGS__}, sizeof((type []){__VA_ARGS__}) / sizeof(type) -#define function static -#define global static -#define local_persist static - #if COMPILER_MSVC #define thread_static __declspec(thread) #elif COMPILER_CLANG || COMPILER_GCC @@ -177,24 +135,6 @@ typedef u64 uptr; (!(f) && (l) ? (f) = (l) : (0)),\ (!(l) && (f) ? (l) = (f) : (0))) -#define KB(a) ((u64)(a) << 10ULL) -#define MB(a) ((u64)(a) << 20ULL) -#define GB(a) ((u64)(a) << 30ULL) - -#define I8_MAX (0x0000007FL) -#define I32_MAX (0x7FFFFFFFL) -#define S32_MAX (0x7FFFFFFFL) -#define U8_MAX (0x000000FFUL) -#define U16_MAX (0x0000FFFFUL) -#define U32_MAX (0xFFFFFFFFUL) -#define U64_MAX (0xFFFFFFFFFFFFFFFFULL) -#define F32_EPSILON (1e-6f) -#ifndef PI - #define PI (3.14159265358979323846f) -#endif - -#include "intrinsics.c" - typedef enum { Axis2_X = 0, Axis2_Y = 1, @@ -211,54 +151,6 @@ typedef alignas(16) union { } u128; typedef enum { - ArenaFlag_NoChain = 1 << 0, - - ArenaFlag_CreationMask = ArenaFlag_NoChain, - - ArenaFlag_Sealed = 1 << 31, -} ArenaFlags; - -typedef struct { - u64 reserve_size; - u64 commit_size; - ArenaFlags flags; - - void *optional_backing_store; - - char *name; - char *allocation_site_file; - i32 allocation_site_line; -} ArenaParameters; - -typedef struct Arena Arena; -struct Arena { - u64 position; - u64 committed; - u64 reserved; - - // NOTE(rnp): arena chain - u64 base_position; // position relative to first arena in chain - Arena *prev; - Arena *current; - - u64 reserve_size; - u64 commit_size; - ArenaFlags flags; - - char *name; - char *allocation_site_file; - i32 allocation_site_line; -}; -typedef struct { Arena *arena; u64 position; } Temp; - -typedef struct { i64 length; u8 *data; } str8; -#define str8(s) (str8){.length = countof(s) - 1, .data = (u8 *)s} -#define str8_comp(s) {sizeof(s) - 1, (u8 *)s} -#define str8_struct(v) (str8){.length = sizeof(*v), .data = (u8 *)v} - -typedef struct { i64 length; u16 *data; } str16; - -typedef enum { StringMatchFlag_CaseInsensitive = (1 << 0), StringMatchFlag_SloppySize = (1 << 1), } StringMatchFlags; @@ -288,85 +180,10 @@ typedef struct { str8 unparsed; } NumberConversion; -typedef struct { u64 start, stop; } RangeU64; - -typedef union { - struct { i32 x, y; }; - struct { i32 w, h; }; - i32 E[2]; -} iv2; - -typedef union { - struct { i32 x, y, z; }; - struct { i32 w, h, d; }; - iv2 xy; - i32 E[3]; -} iv3; - -typedef union { - struct { i32 x, y, z, w; }; - struct { iv3 xyz; i32 _w; }; - i32 E[4]; -} iv4; - -typedef union { - struct { u32 x, y; }; - struct { u32 w, h; }; - u32 E[2]; -} uv2; - -typedef union { - struct { u32 x, y, z; }; - struct { u32 w, h, d; }; - uv2 xy; - u32 E[3]; -} uv3; - -typedef union { - struct { u32 x, y, z, w; }; - struct { uv3 xyz; u32 _w; }; - u32 E[4]; -} uv4; - -typedef union { - struct { b32 x, y, z; }; - b32 E[3]; -} bv3; - -typedef union { - struct { f32 x, y; }; - struct { f32 w, h; }; - f32 E[2]; -} v2; -#define V2_INFINITY (v2){{-inf32(), inf32()}} - -typedef union { - struct { f32 x, y, z; }; - struct { f32 w, h, d; }; - struct { v2 xy; f32 _1; }; - struct { f32 _2; v2 yz; }; - f32 E[3]; -} v3; - -typedef union { - struct { f32 x, y, z, w; }; - struct { f32 r, g, b, a; }; - struct { v3 xyz; f32 _1; }; - struct { f32 _2; v3 yzw; }; - struct { v2 xy, zw; }; - f32 E[4]; -} v4; - #define XZ(v) (v2){.x = v.x, .y = v.z} #define YZ(v) (v2){.x = v.y, .y = v.z} #define XY(v) (v2){.x = v.x, .y = v.y} -typedef union { - struct { v4 x, y, z, w; }; - v4 c[4]; - f32 E[16]; -} m4; - /* TODO(rnp): delete raylib */ typedef struct { v3 origin; @@ -410,13 +227,9 @@ typedef struct { LaneContext lane_context; } ThreadContext; -#define OS_READ_ENTIRE_FILE_FN(name) i64 name(const char *file, void *buffer, i64 buffer_capacity) -#define OS_WAIT_ON_ADDRESS_FN(name) b32 name(i32 *value, i32 current, u32 timeout_ms) -#define OS_WAKE_ALL_WAITERS_FN(name) void name(i32 *sync) #define OS_THREAD_ENTRY_POINT_FN(name) u64 name(void *user_context) #define OS_WRITE_NEW_FILE_FN(name) b32 name(char *fname, str8 raw) -typedef OS_WRITE_NEW_FILE_FN(os_write_new_file_fn); #define RENDERDOC_GET_API_FN(name) b32 name(u32 version, void **out_api) typedef RENDERDOC_GET_API_FN(renderdoc_get_api_fn);