WIP: Getting some of the math sorted out and setting up tick_frametime
This commit is contained in:
@@ -17,7 +17,7 @@ load_client_api :: proc(version_id: int) -> (loaded_module: Client_API) {
|
||||
panic_contextless( "Could not resolve the last write time for sectr")
|
||||
}
|
||||
//TODO(Ed): Lets try to minimize this...
|
||||
thread_sleep( Millisecond * 100 )
|
||||
thread_sleep( Millisecond * 50 )
|
||||
// Get the live dll loaded up
|
||||
file_copy_sync( Path_Sectr_Module, Path_Sectr_Live_Module, allocator = context.temp_allocator )
|
||||
did_load: bool; lib, did_load = os_lib_load( Path_Sectr_Live_Module )
|
||||
@@ -138,8 +138,8 @@ main :: proc()
|
||||
}
|
||||
}
|
||||
barrier_init(& host_memory.lane_job_sync, THREAD_TICK_LANES + THREAD_JOB_WORKERS)
|
||||
host_tick_lane()
|
||||
}
|
||||
host_tick_lane()
|
||||
|
||||
if thread_memory.id == .Master_Prepper {
|
||||
thread_join_multiple(.. host_memory.threads[1:THREAD_TICK_LANES + THREAD_JOB_WORKERS])
|
||||
@@ -183,7 +183,6 @@ host_tick_lane :: proc()
|
||||
for ; sync_load(& host_memory.tick_running, .Relaxed);
|
||||
{
|
||||
profile("Host Tick")
|
||||
sync_client_api()
|
||||
|
||||
running: b64 = host_memory.client_api.tick_lane( duration_seconds(delta_ns), delta_ns ) == false
|
||||
if thread_memory.id == .Master_Prepper {
|
||||
@@ -194,7 +193,8 @@ host_tick_lane :: proc()
|
||||
delta_ns = time_tick_lap_time( & host_tick )
|
||||
host_tick = time_tick_now()
|
||||
|
||||
leader := barrier_wait(& host_memory.lane_sync)
|
||||
// Lanes are synced before doing running check..
|
||||
sync_client_api()
|
||||
}
|
||||
host_lane_shutdown()
|
||||
}
|
||||
@@ -218,12 +218,10 @@ host_job_worker_entrypoint :: proc(worker_thread: ^SysThread)
|
||||
host_memory.client_api.tick_lane_startup(& thread_memory)
|
||||
grime_set_profiler_thread_buffer(& thread_memory.spall_buffer)
|
||||
}
|
||||
// TODO(Ed): We should make sure job system can never be set to "not running" without first draining jobs.
|
||||
for ; sync_load(& host_memory.job_system.running, .Relaxed);
|
||||
{
|
||||
profile("Host Job Tick")
|
||||
host_memory.client_api.jobsys_worker_tick()
|
||||
// TODO(Ed): We cannot allow job threads to enter the reload barrier until all jobs have drained.
|
||||
if sync_load(& host_memory.client_api_hot_reloaded, .Acquire) {
|
||||
// Signals to main hread when all jobs have drained.
|
||||
leader :=barrier_wait(& host_memory.job_hot_reload_sync)
|
||||
@@ -232,6 +230,7 @@ host_job_worker_entrypoint :: proc(worker_thread: ^SysThread)
|
||||
host_memory.client_api.hot_reload(& host_memory, & thread_memory)
|
||||
}
|
||||
}
|
||||
// Were exiting, wait for tick lanes.
|
||||
leader := barrier_wait(& host_memory.lane_job_sync)
|
||||
}
|
||||
|
||||
@@ -256,7 +255,7 @@ sync_client_api :: proc()
|
||||
// Wait for pdb to unlock (linker may still be writting)
|
||||
for ; file_is_locked( Path_Sectr_Debug_Symbols ) && file_is_locked( Path_Sectr_Live_Module ); {}
|
||||
|
||||
thread_sleep( Millisecond * 100 )
|
||||
thread_sleep( Millisecond * 50 )
|
||||
|
||||
host_memory.client_api = load_client_api( version_id )
|
||||
verify( host_memory.client_api.lib_version != 0, "Failed to hot-reload the sectr module" )
|
||||
|
@@ -35,12 +35,9 @@ then prepare for multi-threaded "laned" tick: thread_wide_startup.
|
||||
startup :: proc(host_mem: ^ProcessMemory, thread_mem: ^ThreadMemory)
|
||||
{
|
||||
// Rad Debugger driving me crazy..
|
||||
for ; memory == nil; {
|
||||
memory = host_mem
|
||||
}
|
||||
for ; thread == nil; {
|
||||
thread = thread_mem
|
||||
}
|
||||
// NOTE(Ed): This is problably not necessary, they're just loops for my sanity.
|
||||
for ; memory == nil; { memory = host_mem }
|
||||
for ; thread == nil; { thread = thread_mem }
|
||||
grime_set_profiler_module_context(& memory.spall_context)
|
||||
grime_set_profiler_thread_buffer(& thread.spall_buffer)
|
||||
profile(#procedure)
|
||||
@@ -61,19 +58,21 @@ hot_reload :: proc(host_mem: ^ProcessMemory, thread_mem: ^ThreadMemory)
|
||||
grime_set_profiler_module_context(& memory.spall_context)
|
||||
}
|
||||
else {
|
||||
for ; memory == nil; {
|
||||
sync_load(& memory, .Acquire)
|
||||
}
|
||||
for ; thread == nil; {
|
||||
thread = thread_mem
|
||||
}
|
||||
// NOTE(Ed): This is problably not necessary, they're just loops for my sanity.
|
||||
for ; memory == nil; { sync_load(& memory, .Acquire) }
|
||||
for ; thread == nil; { thread = thread_mem }
|
||||
}
|
||||
grime_set_profiler_thread_buffer(& thread.spall_buffer)
|
||||
}
|
||||
profile(#procedure)
|
||||
// Do hot-reload stuff...
|
||||
{
|
||||
|
||||
// Test dispatching 64 jobs during hot_reload loop (when the above store is uncommented)
|
||||
for job_id := 1; job_id < 64; job_id += 1 {
|
||||
memory.job_info_reload[job_id].id = job_id
|
||||
memory.job_reload[job_id] = make_job_raw(& memory.job_group_reload, & memory.job_info_reload[job_id], test_job, {}, "Job Test (Hot-Reload)")
|
||||
job_dispatch_single(& memory.job_reload[job_id], .Normal)
|
||||
}
|
||||
}
|
||||
// Critical reference synchronization
|
||||
{
|
||||
@@ -82,9 +81,8 @@ hot_reload :: proc(host_mem: ^ProcessMemory, thread_mem: ^ThreadMemory)
|
||||
sync_store(& memory.client_api_hot_reloaded, false, .Release)
|
||||
}
|
||||
else {
|
||||
for ; memory.client_api_hot_reloaded == true; {
|
||||
sync_load(& memory.client_api_hot_reloaded, .Acquire)
|
||||
}
|
||||
// NOTE(Ed): This is problably not necessary, they're just loops for my sanity.
|
||||
for ; memory.client_api_hot_reloaded == true; { sync_load(& memory.client_api_hot_reloaded, .Acquire) }
|
||||
}
|
||||
leader = barrier_wait(& memory.lane_job_sync)
|
||||
}
|
||||
@@ -134,12 +132,6 @@ tick_lane :: proc(host_delta_time_ms: f64, host_delta_ns: Duration) -> (should_c
|
||||
timer += host_delta_time_ms
|
||||
sync_store(& should_close, timer > EXIT_TIME, .Release)
|
||||
|
||||
// Test dispatching 64 jobs during hot_reload loop (when the above store is uncommented)
|
||||
for job_id := 1; job_id < 64; job_id += 1 {
|
||||
memory.job_info_reload[job_id].id = job_id
|
||||
memory.job_reload[job_id] = make_job_raw(& memory.job_group_reload, & memory.job_info_reload[job_id], test_job, {}, "Job Test (Hot-Reload)")
|
||||
job_dispatch_single(& memory.job_reload[job_id], .Normal)
|
||||
}
|
||||
}
|
||||
// profile_end()
|
||||
|
||||
@@ -205,9 +197,69 @@ test_job :: proc(data: rawptr)
|
||||
// log_print_fmt("Test job succeeded: %v", info.id)
|
||||
}
|
||||
|
||||
Frametime_High_Perf_Threshold_MS :: 1 / 240.0
|
||||
|
||||
tick_lane_frametime :: proc()
|
||||
{
|
||||
profile(#procedure)
|
||||
config := app_config()
|
||||
frametime := get_frametime()
|
||||
// context.allocator = frame_slab_allocator()
|
||||
// context.temp_allocator = transient_allocator()
|
||||
|
||||
profile("Client tick timing processing")
|
||||
|
||||
if thread.id == .Master_Prepper
|
||||
{
|
||||
frametime.target_ms = 1.0 / f64(config.engine_refresh_hz) * S_To_MS
|
||||
sub_ms_granularity_required := frametime.target_ms <= Frametime_High_Perf_Threshold_MS
|
||||
|
||||
frametime.delta_ns = time_tick_lap_time( client_tick )
|
||||
frametime.delta_ms = duration_ms( frametime.delta_ns )
|
||||
frametime.delta_seconds = duration_seconds( host_delta_ns )
|
||||
frametime.elapsed_ms = frametime.delta_ms + host_delta_time_ms
|
||||
|
||||
if frametime.elapsed_ms < frametime.target_ms
|
||||
{
|
||||
sleep_ms := frametime.target_ms - frametime.elapsed_ms
|
||||
pre_sleep_tick := time_tick_now()
|
||||
|
||||
if can_sleep && sleep_ms > 0 {
|
||||
// thread_sleep( cast(Duration) sleep_ms * MS_To_NS )
|
||||
// thread__highres_wait( sleep_ms )
|
||||
}
|
||||
|
||||
sleep_delta_ns := time_tick_lap_time( & pre_sleep_tick)
|
||||
sleep_delta_ms := duration_ms( sleep_delta_ns )
|
||||
|
||||
if sleep_delta_ms < sleep_ms {
|
||||
// log( str_fmt_tmp("frametime sleep was off by: %v ms", sleep_delta_ms - sleep_ms ))
|
||||
}
|
||||
|
||||
frametime.elapsed_ms += sleep_delta_ms
|
||||
for ; frametime.elapsed_ms < frametime.target_ms; {
|
||||
sleep_delta_ns = time_tick_lap_time( & pre_sleep_tick)
|
||||
sleep_delta_ms = duration_ms( sleep_delta_ns )
|
||||
|
||||
frametime.elapsed_ms += sleep_delta_ms
|
||||
}
|
||||
}
|
||||
|
||||
config.timing_fps_moving_avg_alpha = 0.99
|
||||
frametime.avg_ms = mov_avg_exp( f64(config.timing_fps_moving_avg_alpha), frametime.elapsed_ms, frametime.avg_ms )
|
||||
frametime.fps_avg = 1 / (frametime.avg_ms * MS_To_S)
|
||||
|
||||
if frametime.elapsed_ms > 60.0 {
|
||||
log_print_fmt("Big tick! %v ms", frametime.elapsed_ms, LoggerLevel.Warning)
|
||||
}
|
||||
|
||||
frametime.current_frame += 1
|
||||
}
|
||||
else
|
||||
{
|
||||
// Non-main thread tick lane timing (since they are in lock-step this should be minimal delta)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@export
|
||||
|
@@ -61,6 +61,8 @@ ThreadMemory :: struct {
|
||||
using _: ThreadWorkerContext,
|
||||
|
||||
// Per-thread profiling
|
||||
spall_buffer_backing: [SPALL_BUFFER_DEFAULT_SIZE * 2]byte,
|
||||
spall_buffer_backing: [SPALL_BUFFER_DEFAULT_SIZE * 4]byte,
|
||||
spall_buffer: Spall_Buffer,
|
||||
|
||||
client_memory: ThreadState,
|
||||
}
|
||||
|
305
code2/sectr/math.odin
Normal file
305
code2/sectr/math.odin
Normal file
@@ -0,0 +1,305 @@
|
||||
package sectr
|
||||
|
||||
/*
|
||||
This is heavy work-in-progress personalized math definitions.
|
||||
|
||||
Desire is for the definitions to be from a geo alg / clifford alg lens instead of linear alg.
|
||||
Want to maximize use of optimal linear alg operations in the defs though already defined by odin's linear alg library.
|
||||
|
||||
I apologize if this looks terrible my intuiton for math is very sub-par symbolically.
|
||||
*/
|
||||
|
||||
import "base:intrinsics"
|
||||
import "core:math"
|
||||
import la "core:math/linalg"
|
||||
|
||||
Axis2 :: enum i32 {
|
||||
Invalid = -1,
|
||||
X = 0,
|
||||
Y = 1,
|
||||
Count,
|
||||
}
|
||||
|
||||
f32_Infinity :: 0x7F800000
|
||||
f32_Min :: 0x00800000
|
||||
|
||||
// Note(Ed) : I don't see an intrinsict available anywhere for this. So I'll be using the Terathon non-sse impl
|
||||
// Inverse Square Root
|
||||
// C++ Source https://github.com/EricLengyel/Terathon-Math-Library/blob/main/TSMath.cpp#L191
|
||||
inverse_sqrt_f32 :: proc "contextless" ( value: f32 ) -> f32
|
||||
{
|
||||
if ( value < f32_Min) { return f32_Infinity }
|
||||
value_u32 := transmute(u32) value
|
||||
|
||||
initial_approx := 0x5F375A86 - (value_u32 >> 1)
|
||||
refined_approx := transmute(f32) initial_approx
|
||||
|
||||
// Newton–Raphson method for getting better approximations of square roots
|
||||
// Done twice for greater accuracy.
|
||||
refined_approx = refined_approx * (1.5 - value * 0.5 * refined_approx * refined_approx )
|
||||
refined_approx = refined_approx * (1.5 - value * 0.5 * refined_approx * refined_approx )
|
||||
// refined_approx = (0.5 * refined_approx) * (3.0 - value * refined_approx * refined_approx)
|
||||
// refined_approx = (0.5 * refined_approx) * (3.0 - value * refined_approx * refined_approx)
|
||||
return refined_approx
|
||||
}
|
||||
|
||||
is_power_of_two_u32 :: #force_inline proc "contextless" (value: u32) -> b32 { return value != 0 && ( value & ( value - 1 )) == 0 }
|
||||
|
||||
mov_avg_exp_f32 := #force_inline proc "contextless" (alpha, delta_interval, last_value: f32) -> f32 { return (delta_interval * alpha) + (delta_interval * (1.0 - alpha)) }
|
||||
mov_avg_exp_f64 := #force_inline proc "contextless" (alpha, delta_interval, last_value: f64) -> f64 { return (delta_interval * alpha) + (delta_interval * (1.0 - alpha)) }
|
||||
|
||||
Q_F4 :: quaternion128
|
||||
V2_S4 :: [2]i32
|
||||
V3_S4 :: [3]i32
|
||||
|
||||
M2_F4 :: matrix [2, 2] f32 // Column Major
|
||||
R2_F4 :: struct { p0, p1: V2_F4 } // Column Major (they are equivalnet)
|
||||
UR2_F4 :: distinct R2_F4
|
||||
|
||||
r2f4_zero :: R2_F4 {}
|
||||
|
||||
r2f4 :: #force_inline proc "contextless" (a, b: V2_F4) -> R2_F4 { return R2_F4{a, b} }
|
||||
|
||||
m2f4_from_r2f4 :: #force_inline proc "contextless" (range: R2_F4) -> M2_F4 { return transmute(M2_F4)range }
|
||||
r2f4_from_m2f4 :: #force_inline proc "contextless" (m: M2_F4) -> R2_F4 { return transmute(R2_F4)m }
|
||||
|
||||
add_r2f4 :: #force_inline proc "contextless" (a, b: R2_F4) -> R2_F4 { return r2f4_from_m2f4(m2f4_from_r2f4(a) + m2f4_from_r2f4(b)) }
|
||||
sub_r2f4 :: #force_inline proc "contextless" (a, b: R2_F4) -> R2_F4 { return r2f4_from_m2f4(m2f4_from_r2f4(a) - m2f4_from_r2f4(b)) }
|
||||
equal_r2f4 :: #force_inline proc "contextless" (a, b: R2_F4) -> b32 { result := a.p0 == b.p0 && a.p1 == b.p1; return b32(result) }
|
||||
|
||||
// Will resolve the largest range possible given a & b.
|
||||
join_r2f4 :: #force_inline proc "contextless" (a, b: R2_F4) -> (joined : R2_F4) { joined.p0 = min( a.min, b.min ); joined.p1 = max( a.max, b.max ); return }
|
||||
size_range2 :: #force_inline proc "contextless" (value: R2_F4) -> V2_F4 { return { abs( value.p1.x - value.p0.x ), abs( value.p0.y - value.p1.y ) } }
|
||||
|
||||
cross_s :: la.scalar_cross
|
||||
|
||||
/*
|
||||
V2_F2: 2D Vector (4-Byte Float) 4D Extension (x, y, z : 0, w : 0)
|
||||
BV2_F2: 2D Bivector (4-Byte Float)
|
||||
T2_F2: 3x3 Matrix (4-Byte Float) where 3rd row is always (0, 0, 1)
|
||||
Rotor2_F4: Rotor 2D (4-Byte Float) s is scalar.
|
||||
*/
|
||||
V2_F4 :: [2]f32
|
||||
BiV2_F4 :: distinct f32
|
||||
T2_F4 :: matrix [3, 3] f32
|
||||
UV2_F4 :: distinct V2_F4
|
||||
Rotor2_F4 :: struct { bv: BiV2_F4, s: f32 }
|
||||
|
||||
rotor2f4_to_complex64 :: #force_inline proc "contextless" (rotor: Rotor2_F4) -> complex64 { return transmute(complex64) rotor; }
|
||||
|
||||
v2f4_from_f32s :: #force_inline proc "contextless" (x, y: f32 ) -> V2_F4 { return {x, y} }
|
||||
v2f4_from_scalar :: #force_inline proc "contextless" (scalar: f32 ) -> V2_F4 { return {scalar, scalar}}
|
||||
v2f4_from_v2s4 :: #force_inline proc "contextless" (v2i: V2_S4) -> V2_F4 { return {f32(v2i.x), f32(v2i.y)}}
|
||||
v2s4_from_v2f4 :: #force_inline proc "contextless" (v2: V2_F4) -> V2_S4 { return {i32(v2.x), i32(v2.y) }}
|
||||
|
||||
// vec2_64_from_vec2 :: #force_inline proc "contextless" ( v2 : Vec2 ) -> Vec2_64 { return { f64(v2.x), f64(v2.y) }}
|
||||
|
||||
// dot_v2f4 :: #force_inline proc "contextless" (a, b: V2_F4) -> (s: f32) {
|
||||
// x := a.x * b.x
|
||||
// y := a.y + b.y
|
||||
// s = x + y
|
||||
// return
|
||||
// }
|
||||
sdot :: la.scalar_dot
|
||||
vdot :: la.vector_dot
|
||||
qdot_f2 :: la.quaternion64_dot
|
||||
qdot_f4 :: la.quaternion128_dot
|
||||
qdot_f8 :: la.quaternion256_dot
|
||||
inner_product :: dot
|
||||
outer_product :: intrinsics.outer_product
|
||||
|
||||
cross_v2 :: la.vector_cross2
|
||||
|
||||
/*
|
||||
PointFlat2 : CGA: 2D flat point (x, y, z)
|
||||
Line : PGA: 2D line (x, y, z)
|
||||
*/
|
||||
|
||||
P2_F4 :: distinct V2_F4
|
||||
PF2_F4 :: distinct V3_F4
|
||||
L2_F4 :: distinct V3_F4
|
||||
|
||||
//endregion PGA 2
|
||||
|
||||
//region PGA 3
|
||||
/*
|
||||
V3_F4: 3D Vector (x, y, z) (3x1) 4D Expression : (x, y, z, 0)
|
||||
BiV3_F4: 3D Bivector (yz, zx, xy) (3x1)
|
||||
TriV3_F4: 3D Trivector (xyz) (1x1)
|
||||
Rotor3: 3D Rotation Versor-Transform (4x1)
|
||||
Motor3: 3D Rotation & Translation Transform (4x2)
|
||||
*/
|
||||
|
||||
V3_F4 :: [3]f32
|
||||
V4_F4 :: [4]f32
|
||||
|
||||
BiV3_F4 :: struct #raw_union {
|
||||
using _ : struct { yz, zx, xy : f32 },
|
||||
using xyz : V3_F4,
|
||||
}
|
||||
|
||||
TriV3_F4 :: distinct f32
|
||||
|
||||
Rotor3_F4 :: struct {
|
||||
using bv: BiV3_F4,
|
||||
s: f32, // Scalar
|
||||
}
|
||||
|
||||
Shifter3_F4 :: struct {
|
||||
using bv: BiV3_F4,
|
||||
s: f32, // Scalar
|
||||
}
|
||||
|
||||
Motor3 :: struct {
|
||||
rotor: Rotor3_F4,
|
||||
md: Shifter3_F4,
|
||||
}
|
||||
|
||||
UV3_F4 :: distinct V3_F4
|
||||
UV4_F4 :: distinct V4_F4
|
||||
UBiV3_F4 :: distinct BiV3_F4
|
||||
|
||||
//region Vec3
|
||||
|
||||
v3f4_via_f32s :: #force_inline proc "contextless" (x, y, z: f32) -> V3_F4 { return {x, y, z} }
|
||||
|
||||
// complement_vec3 :: #force_inline proc "contextless" ( v : Vec3 ) -> Bivec3 {return transmute(Bivec3) v}
|
||||
|
||||
cross_v3 :: la.vector_cross3
|
||||
|
||||
inverse_mag_v3f4 :: #force_inline proc "contextless" (v: V3_F4) -> (result : f32) { square := pow2_v3f4(v); result = inverse_sqrt_f32( square ); return }
|
||||
magnitude_v3f4 :: #force_inline proc "contextless" (v: V3_F4) -> (mag: f32) { square := pow2_v3f4(v); mag = sqrt(square); return }
|
||||
normalize_v3f4 :: #force_inline proc "contextless" (v: V3_F4) -> (unit_v: UV3_F4) { unit_v = transmute(UnitVec3) (v * inverse_mag_v3f4(v)); return }
|
||||
|
||||
pow2_v3f4 :: #force_inline proc "contextless" (v: V3_F4) -> (s: f32) { return dot_v3f4(v, v) }
|
||||
|
||||
project_v3f4 :: proc "contextless" (a, b: V3_F4) -> (a_to_b: V3_F4) { panic_contextless("not implemented"); return }
|
||||
reject_v3f4 :: proc "contextless" (a, b: V3_F4 ) -> (a_from_b: V3_F4) { panic_contextless("not implemented"); return }
|
||||
|
||||
project_v3f4_uv3f4 :: #force_inline proc "contextless" (v: V3_F4, u: UV3_F4) -> (v_to_u: V3_F4) { inner := dot_v3f4(v, u); v_to_u = (transmute(V3_F4) u) * inner; return }
|
||||
project_uv3f4_v3f4 :: #force_inline proc "contextless" (u : UV3_F4, v : V3_F4) -> (u_to_v: V3_F4) { inner := dot_v3f4(u, v); u_to_v = v * inner; return }
|
||||
|
||||
// Anti-wedge of vectors
|
||||
regress_v3f4 :: #force_inline proc "contextless" (a, b : V3_F4) -> f32 { return a.x * b.y - a.y * b.x }
|
||||
|
||||
reject_v3f4_uv3f4 :: #force_inline proc "contextless" (v: V3_F4, u: UV3_F4) -> ( v_from_u: V3_F4) { inner := dot_v3f4(v, u); v_from_u = (v - (transmute(Vec3) u)) * inner; return }
|
||||
reject_uv3f4_v3f4 :: #force_inline proc "contextless" (v: V3_F4, u: UV3_F4) -> ( u_from_v: V3_F4) { inner := dot_v3f4(u, v); u_from_v = ((transmute(Vec3) u) - v) * inner; return }
|
||||
|
||||
// Combines the deimensions that are present in a & b
|
||||
wedge_v3f4 :: #force_inline proc "contextless" (a, b: V3_F4) -> (bv : BiV3_F4) {
|
||||
yzx_zxy := a.yzx * b.zxy
|
||||
zxy_yzx := a.zxy * b.yzx
|
||||
bv = transmute(Bivec3) (yzx_zxy - zxy_yzx)
|
||||
return
|
||||
}
|
||||
|
||||
//endregion Vec3
|
||||
|
||||
//region Bivec3
|
||||
biv3f4_via_f32s :: #force_inline proc "contextless" (yz, zx, xy : f32) -> BiV3_F4 {return { xyz = {yz, zx, xy} }}
|
||||
|
||||
complement_biv3f4 :: #force_inline proc "contextless" (b : BiV3_F4) -> BiV3_F4 {return transmute(BiV3_F4) b.xyz}
|
||||
|
||||
//region Operations isomoprhic to vectors
|
||||
negate_biv3f4 :: #force_inline proc "contextless" (b : BiV3_F4) -> BiV3_F4 {return transmute(BiV3_F4) -b.xyz}
|
||||
add_biv3f4 :: #force_inline proc "contextless" (a, b: BiV3_F4) -> BiV3_F4 {return transmute(BiV3_F4) (a.xyz + b.xyz)}
|
||||
sub_biv3f4 :: #force_inline proc "contextless" (a, b: BiV3_F4) -> BiV3_F4 {return transmute(BiV3_F4) (a.xyz - b.xyz)}
|
||||
mul_biv3f4 :: #force_inline proc "contextless" (a, b: BiV3_F4) -> BiV3_F4 {return transmute(BiV3_F4) (a.xyz * b.xyz)}
|
||||
mul_biv3f4_f32 :: #force_inline proc "contextless" (b: BiV3_F4, s: f32) -> BiV3_F4 {return transmute(BiV3_F4) (b.xyz * s)}
|
||||
mul_f32_biv3f4 :: #force_inline proc "contextless" (s: f32, b: BiV3_F4) -> BiV3_F4 {return transmute(BiV3_F4) (s * b.xyz)}
|
||||
div_biv3f4_f32 :: #force_inline proc "contextless" (b: BiV3_F4, s: f32) -> BiV3_F4 {return transmute(BiV3_F4) (b.xyz / s)}
|
||||
inverse_mag_biv3f4 :: #force_inline proc "contextless" (b: BiV3_F4) -> f32 {return inverse_mag_vec3(b.xyz)}
|
||||
magnitude_biv3f4 :: #force_inline proc "contextless" (b: BiV3_F4) -> f32 {return magnitude_vec3 (b.xyz)}
|
||||
normalize_biv3f4 :: #force_inline proc "contextless" (b: BiV3_F4) -> UBiV3_F4 {return transmute(UBiV3_F4) normalize_vec3(b.xyz)}
|
||||
squared_mag_biv3f4 :: #force_inline proc "contextless" (b: BiV3_F4) -> f32 {return pow2_vec3(b.xyz)}
|
||||
//endregion Operations isomoprhic to vectors
|
||||
|
||||
// The wedge of a bi-vector in 3D vector space results in a Trivector represented as a scalar.
|
||||
// This scalar usually resolves to zero with six possible exceptions that lead to the negative volume element.
|
||||
wedge_biv3f4 :: #force_inline proc "contextless" (a, b: BiV3_F4) -> f32 { s := a.yz + b.yz + a.zx + b.zx + a.xy + b.xy; return s }
|
||||
|
||||
// anti-wedge (Combines dimensions that are absent from a & b)
|
||||
regress_biv3f4 :: #force_inline proc "contextless" (a, b: BiV3_F4) -> V3_F4 {return wedge_vec3(vec3(a), vec3(b))}
|
||||
regress_biv3f4_v3f4 :: #force_inline proc "contextless" (b: BiV3_F4, v: V3_F4) -> f32 {return regress_vec3(b.xyz, v)}
|
||||
regress_v3_biv3f4 :: #force_inline proc "contextless" (v: V3_F4, b: BiV3_F4) -> f32 {return regress_vec3(b.xyz, v)}
|
||||
|
||||
//endregion biv3f4
|
||||
|
||||
//region Rotor3
|
||||
|
||||
rotor3f4_via_comps_f4 :: proc "contextless" (yz, zx, xy, scalar : f32) -> Rotor3_F4 { return Rotor3 {biv3f4_via_f32s(yz, zx, xy), scalar} }
|
||||
|
||||
rotor3f4_via_bv_s_f4 :: #force_inline proc "contextless" (bv: BiV3_F4, scalar: f32) -> (rotor : Rotor3_F4) { return Rotor3_F4 {bv, scalar} }
|
||||
// rotor3f4_via_from_to_v3f4 :: #force_inline proc "contextless" (from, to: V3_F4) -> (rotor : Rotor3_F4) { rotor.scalar := 1 + dot( from, to ); return }
|
||||
|
||||
inverse_mag_rotor3f4 :: proc "contextless" (rotor : Rotor3_F4) -> (s : f32) { panic("not implemented"); return }
|
||||
magnitude_rotor3f4 :: proc "contextless" (rotor : Rotor3_F4) -> (s : f32) { panic("not implemented"); return }
|
||||
squared_mag_f4 :: proc "contextless" (rotor : Rotor3_F4) -> (s : f32) { panic("not implemented"); return }
|
||||
reverse_rotor3_f4 :: proc "contextless" (rotor : Rotor3_F4) -> (reversed : Rotor3_F4) { reversed = { negate_biv3f4(rotor.bv), rotor.s }; return }
|
||||
|
||||
//endregion Rotor3
|
||||
|
||||
//region Flat Projective Geometry
|
||||
|
||||
Point3_F4 :: distinct V3_F4
|
||||
PointFlat3_F4 :: distinct V4_F4
|
||||
Line3_F4 :: struct {
|
||||
weight : V3_F4,
|
||||
bulk : BiV3_F4,
|
||||
}
|
||||
Plane3_F4 :: distinct V4_F4 // 4D Anti-vector
|
||||
|
||||
// aka: wedge operation for points
|
||||
join_point3_f4 :: proc "contextless" (p, q : Point3_F4) -> (l : Line3_F4) {
|
||||
weight := sub(q, p)
|
||||
bulk := wedge(vec3(p), vec3(q))
|
||||
l = {weight, bulk}
|
||||
return
|
||||
}
|
||||
join_pointflat3_f4 :: proc "contextless" (p, q : PointFlat3_F4) -> (l : Line3_F4) {
|
||||
weight := vec3(
|
||||
p.w * q.x - p.x * q.w,
|
||||
p.w * q.y - p.y * q.w,
|
||||
p.w * q.z - p.z * q.w
|
||||
)
|
||||
bulk := wedge(vec3(p), vec3(q))
|
||||
l = { weight, bulk}
|
||||
return
|
||||
}
|
||||
sub_point3_f4 :: #force_inline proc "contextless" (a, b : Point3_F4) -> (v : V3_F4) { v = v3f4(a) - v3f4(b); return }
|
||||
|
||||
//endregion Flat Projective Geometry
|
||||
|
||||
//region Rational Trig
|
||||
|
||||
quadrance :: proc "contextless" (a, b : Point3_F4) -> (q : f32) { q = pow2( sub(a, b)); return }
|
||||
|
||||
// Assumes the weight component is normalized.
|
||||
spread :: proc "contextless" (l, m : Line3_F4) -> (s : f32) { s = dot(l.weight, m.weight); return }
|
||||
|
||||
//endregion Rational Trig
|
||||
|
||||
//region Grime
|
||||
// A dump of equivalent symbol generatioon (because the toolchain can't do it yet)
|
||||
// Symbol alias tables are in grim.odin
|
||||
|
||||
v3f4_to_biv3f4 :: #force_inline proc "contextless" (v: V3_F4) -> BiV3_F4 {return transmute(Bivec3) v }
|
||||
biv3f4_to_v3f4 :: #force_inline proc "contextless" (bv: BiV3_F4) -> V3_F4 {return transmute(Vec3) bv }
|
||||
quatf4_from_rotor3f4 :: #force_inline proc "contextless" (rotor: Rotor3_F4) -> Q_F4 {return transmute(Quat128) rotor}
|
||||
uv3f4_to_v3f4 :: #force_inline proc "contextless" (v: UV3_F4) -> V3_F4 {return transmute(Vec3) v }
|
||||
uv4f4_to_v4f4 :: #force_inline proc "contextless" (v: UV4_F4) -> V4_F4 {return transmute(Vec4) v }
|
||||
|
||||
// plane_to_v4f4 :: #force_inline proc "contextless" (p : Plane3_F4) -> V4_F4 {return transmute(V4_F4) p}
|
||||
point3f4_to_v3f4 :: #force_inline proc "contextless" (p: Point3_F4) -> V3_F4 {return transmute(Vec3) p}
|
||||
pointflat3f4_to_v3f4 :: #force_inline proc "contextless" (p: PointFlat3_F4) -> V3_F4 {return { p.x, p.y, p.z }}
|
||||
v3f4_to_point3f4 :: #force_inline proc "contextless" (v: V3_F4) -> Point3_F4 {return transmute(Point3) v}
|
||||
|
||||
cross_v3f4_uv3f4 :: #force_inline proc "contextless" (v: V3_F4, u: UV3_F4) -> V3_F4 {return cross_v3f4(v, transmute(Vec3) u)}
|
||||
cross_u3f4_v3f4 :: #force_inline proc "contextless" (u: UV3_F4, v: V3_F4) -> V3_F4 {return cross_v3f4(transmute(Vec3) u, v)}
|
||||
|
||||
dot_v3f4_uv3f4 :: #force_inline proc "contextless" (v: V3_F4, unit_v: UV3_F4) -> f32 {return dot_v3f4(v, transmute(Vec3) unit_v)}
|
||||
dot_uv3f4_vs :: #force_inline proc "contextless" (unit_v: UV3_F4, v: V3_F4) -> f32 {return dot_v3f4(v, transmute(Vec3) unit_v)}
|
||||
|
||||
wedge_v3f4_uv3f4 :: #force_inline proc "contextless" (v : V3_F4, unit_v: UV3_F4) -> BiV3_F4 {return wedge_v3f4(v, transmute(Vec3) unit_v)}
|
||||
wedge_uv3f4_vs :: #force_inline proc "contextless" (unit_v: UV3_F4, v: V3_F4) -> BiV3_F4 {return wedge_v3f4(transmute(Vec3) unit_v, v)}
|
||||
//endregion Grime
|
@@ -3,8 +3,6 @@ package sectr
|
||||
/*
|
||||
All direct non-codebase package symbols should do zero allocations.
|
||||
Any symbol that does must be mapped from the Grime package to properly tirage its allocator to odin's ideomatic interface.
|
||||
|
||||
|
||||
*/
|
||||
|
||||
import "base:intrinsics"
|
||||
@@ -64,6 +62,24 @@ Mega :: Kilo * 1024
|
||||
Giga :: Mega * 1024
|
||||
Tera :: Giga * 1024
|
||||
|
||||
// chrono
|
||||
NS_To_MS :: grime.NS_To_MS
|
||||
NS_To_US :: grime.NS_To_US
|
||||
NS_To_S :: grime.NS_To_S
|
||||
|
||||
US_To_NS :: grime.US_To_NS
|
||||
US_To_MS :: grime.US_To_MS
|
||||
US_To_S :: grime.US_To_S
|
||||
|
||||
MS_To_NS :: grime.MS_To_NS
|
||||
MS_To_US :: grime.MS_To_US
|
||||
MS_To_S :: grime.MS_To_S
|
||||
|
||||
S_To_NS :: grime.S_To_NS
|
||||
S_To_US :: grime.S_To_US
|
||||
S_To_MS :: grime.S_To_MS
|
||||
|
||||
|
||||
ensure :: #force_inline proc( condition : b32, msg : string, location := #caller_location ) {
|
||||
if condition do return
|
||||
log_print( msg, LoggerLevel.Warning, location )
|
||||
@@ -106,3 +122,111 @@ profile_begin :: #force_inline proc "contextless" ( name : string, loc := #calle
|
||||
profile_end :: #force_inline proc "contextless" () {
|
||||
spall._buffer_end( & memory.spall_context, & thread.spall_buffer)
|
||||
}
|
||||
|
||||
|
||||
|
||||
add :: proc {
|
||||
add_r2f4,
|
||||
}
|
||||
|
||||
biv3f4 :: proc {
|
||||
biv3f4_via_f32s,
|
||||
v3f4_to_biv3f4,
|
||||
}
|
||||
bivec :: biv3f4
|
||||
|
||||
cross :: proc {
|
||||
cross_s,
|
||||
cross_v2,
|
||||
cross_v3,
|
||||
}
|
||||
|
||||
dot :: proc {
|
||||
sdot,
|
||||
vdot,
|
||||
qdot_f2,
|
||||
qdot_f4,
|
||||
qdot_f8,
|
||||
}
|
||||
|
||||
is_power_of_two :: proc {
|
||||
is_power_of_two_u32,
|
||||
// is_power_of_two_uintptr,
|
||||
}
|
||||
|
||||
mov_avg_exp :: proc {
|
||||
mov_avg_exp_f32,
|
||||
mov_avg_exp_f64,
|
||||
}
|
||||
|
||||
join :: proc {
|
||||
join_r2f4,
|
||||
}
|
||||
|
||||
inverse_sqrt :: proc {
|
||||
inverse_sqrt_f32,
|
||||
}
|
||||
|
||||
sub :: proc {
|
||||
sub_r2f4,
|
||||
sub_biv3f4,
|
||||
join_point3_f4,
|
||||
join_pointflat3_f4,
|
||||
}
|
||||
|
||||
pow2 :: proc {
|
||||
pow2_v3f4,
|
||||
}
|
||||
|
||||
regress :: proc {
|
||||
regress_biv3f4,
|
||||
}
|
||||
|
||||
rotor3 :: proc {
|
||||
rotor3f4_via_comps_f4,
|
||||
rotor3f4_via_bv_s_f4,
|
||||
// rotor3f4_via_from_to_v3f4,
|
||||
}
|
||||
|
||||
quatf4 :: proc {
|
||||
quatf4_from_rotor3f4,
|
||||
}
|
||||
|
||||
v2f4 :: proc {
|
||||
v2f4_from_f32s,
|
||||
v2f4_from_scalar,
|
||||
v2f4_from_v2s4,
|
||||
v2s4_from_v2f4,
|
||||
}
|
||||
|
||||
v3f4 :: proc {
|
||||
v3f4_via_f32s,
|
||||
biv3f4_to_v3f4,
|
||||
point3f4_to_v3f4,
|
||||
pointflat3f4_to_v3f4,
|
||||
uv3f4_to_v3f4,
|
||||
}
|
||||
|
||||
v2 :: proc {
|
||||
v2f4_from_f32s,
|
||||
v2f4_from_scalar,
|
||||
v2f4_from_v2s4,
|
||||
v2s4_from_v2f4,
|
||||
}
|
||||
|
||||
v3 :: proc {
|
||||
v3f4_via_f32s,
|
||||
biv3f4_to_v3f4,
|
||||
point3f4_to_v3f4,
|
||||
pointflat3f4_to_v3f4,
|
||||
uv3f4_to_v3f4,
|
||||
}
|
||||
|
||||
v4 :: proc {
|
||||
uv4f4_to_v4f4,
|
||||
}
|
||||
|
||||
wedge :: proc {
|
||||
wedge_v3f4,
|
||||
wedge_biv3f4,
|
||||
}
|
||||
|
57
code2/sectr/space.odin
Normal file
57
code2/sectr/space.odin
Normal file
@@ -0,0 +1,57 @@
|
||||
package sectr
|
||||
|
||||
/* Space
|
||||
Provides various definitions for converting from one standard of measurement to another.
|
||||
Provides constructs and transformations in reguards to space.
|
||||
|
||||
Ultimately the user's window ppcm (pixels-per-centimeter) determins how all virtual metric conventions are handled.
|
||||
*/
|
||||
|
||||
// The points to pixels and pixels to points are our only reference to accurately converting
|
||||
// an object from world space to screen-space.
|
||||
// This prototype engine will have all its spacial unit base for distances in virtual pixels.
|
||||
|
||||
Inches_To_CM :: cast(f32) 2.54
|
||||
Points_Per_CM :: cast(f32) 28.3465
|
||||
CM_Per_Point :: cast(f32) 1.0 / DPT_DPCM
|
||||
CM_Per_Pixel :: cast(f32) 1.0 / DPT_PPCM
|
||||
DPT_DPCM :: cast(f32) 72.0 * Inches_To_CM // 182.88 points/dots per cm
|
||||
DPT_PPCM :: cast(f32) 96.0 * Inches_To_CM // 243.84 pixels per cm
|
||||
|
||||
when ODIN_OS == .Windows {
|
||||
op_default_dpcm :: 72.0 * Inches_To_CM
|
||||
os_default_ppcm :: 96.0 * Inches_To_CM
|
||||
// 1 inch = 2.54 cm, 96 inch * 2.54 = 243.84 DPCM
|
||||
}
|
||||
|
||||
AreaSize :: V2_F4
|
||||
|
||||
Bounds2 :: struct {
|
||||
top_left, bottom_right: V2_F4,
|
||||
}
|
||||
|
||||
BoundsCorners2 :: struct {
|
||||
top_left, top_right, bottom_left, bottom_right: V2_F4,
|
||||
}
|
||||
|
||||
E2_F4 :: V2_F4
|
||||
E2_S4 :: V2_F4
|
||||
|
||||
WS_Pos :: struct {
|
||||
tile_id : V2_S4,
|
||||
rel : V2_F4,
|
||||
}
|
||||
|
||||
Camera :: struct {
|
||||
view : E2_F4,
|
||||
position : V2_F4,
|
||||
zoom : f32,
|
||||
}
|
||||
|
||||
Camera_Default := Camera { zoom = 1 }
|
||||
|
||||
CameraZoomMode :: enum u32 {
|
||||
Digital,
|
||||
Smooth,
|
||||
}
|
||||
|
@@ -6,5 +6,76 @@ package sectr
|
||||
@(thread_local) thread: ^ThreadMemory
|
||||
//endregion STATIC MEMORy
|
||||
|
||||
State :: struct {
|
||||
MemoryConfig :: struct {
|
||||
reserve_persistent : uint,
|
||||
reserve_frame : uint,
|
||||
reserve_transient : uint,
|
||||
reserve_filebuffer : uint,
|
||||
|
||||
commit_initial_persistent : uint,
|
||||
commit_initial_frame : uint,
|
||||
commit_initial_transient : uint,
|
||||
commit_initial_filebuffer : uint,
|
||||
}
|
||||
|
||||
// All nobs available for this application
|
||||
AppConfig :: struct {
|
||||
using memory : MemoryConfig,
|
||||
|
||||
resolution_width : uint,
|
||||
resolution_height : uint,
|
||||
refresh_rate : uint,
|
||||
|
||||
cam_min_zoom : f32,
|
||||
cam_max_zoom : f32,
|
||||
cam_zoom_mode : CameraZoomMode,
|
||||
cam_zoom_smooth_snappiness : f32,
|
||||
cam_zoom_sensitivity_smooth : f32,
|
||||
cam_zoom_sensitivity_digital : f32,
|
||||
cam_zoom_scroll_delta_scale : f32,
|
||||
|
||||
engine_refresh_hz : uint,
|
||||
|
||||
timing_fps_moving_avg_alpha : f32,
|
||||
|
||||
ui_resize_border_width : f32,
|
||||
|
||||
// color_theme : AppColorTheme,
|
||||
|
||||
text_snap_glyph_shape_position : b32,
|
||||
text_snap_glyph_render_height : b32,
|
||||
text_size_screen_scalar : f32,
|
||||
text_size_canvas_scalar : f32,
|
||||
text_alpha_sharpen : f32,
|
||||
}
|
||||
|
||||
FrameTime :: struct {
|
||||
sleep_is_granular : b32,
|
||||
|
||||
current_frame : u64,
|
||||
delta_seconds : f64,
|
||||
delta_ms : f64,
|
||||
delta_ns : Duration,
|
||||
target_ms : f64,
|
||||
elapsed_ms : f64,
|
||||
avg_ms : f64,
|
||||
fps_avg : f64,
|
||||
}
|
||||
|
||||
State :: struct {
|
||||
config: AppConfig,
|
||||
|
||||
// Overall frametime of the tick frame (currently main thread's)
|
||||
using frametime : FrameTime,
|
||||
}
|
||||
|
||||
ThreadState :: struct {
|
||||
delta_seconds: f64,
|
||||
delta_ms: f64,
|
||||
delta_ns: Duration,
|
||||
elapsed_ms: f64,
|
||||
avg_ms: f64,
|
||||
}
|
||||
|
||||
app_config :: #force_inline proc "contextless" () -> AppConfig { return memory.client_memory.config }
|
||||
get_frametime :: #force_inline proc "contextless" () -> FrameTime { return memory.client_memory.frametime }
|
||||
|
Reference in New Issue
Block a user