Setup spall profiling, did first optimizations!
This commit is contained in:
parent
304e710c16
commit
1656dffb67
116
code/api.odin
116
code/api.odin
@ -9,6 +9,7 @@ import "core:os"
|
||||
import "core:slice"
|
||||
import "core:strings"
|
||||
import "core:time"
|
||||
import "core:prof/spall"
|
||||
import rl "vendor:raylib"
|
||||
|
||||
Path_Assets :: "../assets/"
|
||||
@ -27,8 +28,11 @@ ModuleAPI :: struct {
|
||||
}
|
||||
|
||||
@export
|
||||
startup :: proc( persistent_mem, frame_mem, transient_mem, files_buffer_mem : ^VArena, host_logger : ^ Logger )
|
||||
startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem, files_buffer_mem : ^VArena, host_logger : ^ Logger )
|
||||
{
|
||||
spall.SCOPED_EVENT( & prof.ctx, & prof.buffer, #procedure )
|
||||
Memory_App.profiler = prof
|
||||
|
||||
startup_tick := time.tick_now()
|
||||
|
||||
logger_init( & Memory_App.logger, "Sectr", host_logger.file_path, host_logger.file )
|
||||
@ -221,8 +225,11 @@ sectr_shutdown :: proc()
|
||||
}
|
||||
|
||||
@export
|
||||
reload :: proc( persistent_mem, frame_mem, transient_mem, files_buffer_mem : ^VArena, host_logger : ^ Logger )
|
||||
reload :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem, files_buffer_mem : ^VArena, host_logger : ^ Logger )
|
||||
{
|
||||
spall.SCOPED_EVENT( & prof.ctx, & prof.buffer, #procedure )
|
||||
Memory_App.profiler = prof
|
||||
|
||||
context.logger = to_odin_logger( & Memory_App.logger )
|
||||
using Memory_App;
|
||||
|
||||
@ -234,13 +241,14 @@ reload :: proc( persistent_mem, frame_mem, transient_mem, files_buffer_mem : ^VA
|
||||
context.allocator = persistent_allocator()
|
||||
context.temp_allocator = transient_allocator()
|
||||
|
||||
state := get_state()
|
||||
|
||||
// Procedure Addresses are not preserved on hot-reload. They must be restored for persistent data.
|
||||
// The only way to alleviate this is to either do custom handles to allocators
|
||||
// Or as done below, correct containers using allocators on reload.
|
||||
// Thankfully persistent dynamic allocations are rare, and thus we know exactly which ones they are.
|
||||
|
||||
font_provider_data := & get_state().font_provider_data
|
||||
font_provider_data := & state.font_provider_data
|
||||
font_provider_data.font_cache.hashes.allocator = persistent_slab_allocator()
|
||||
font_provider_data.font_cache.entries.allocator = persistent_slab_allocator()
|
||||
|
||||
@ -257,73 +265,83 @@ swap :: proc( a, b : ^ $Type ) -> ( ^ Type, ^ Type ) {
|
||||
@export
|
||||
tick :: proc( host_delta_time : f64, host_delta_ns : Duration ) -> b32
|
||||
{
|
||||
profile( "Client Tick" )
|
||||
context.logger = to_odin_logger( & Memory_App.logger )
|
||||
state := get_state(); using state
|
||||
|
||||
client_tick := time.tick_now()
|
||||
should_close : b32
|
||||
|
||||
// Setup Frame Slab
|
||||
client_tick := time.tick_now()
|
||||
{
|
||||
alloc_error : AllocatorError
|
||||
frame_slab, alloc_error = slab_init( & default_slab_policy, bucket_reserve_num = 0, allocator = frame_allocator() )
|
||||
verify( alloc_error == .None, "Failed to allocate frame slab" )
|
||||
profile("Work frame")
|
||||
|
||||
// Setup Frame Slab
|
||||
{
|
||||
alloc_error : AllocatorError
|
||||
frame_slab, alloc_error = slab_init( & default_slab_policy, bucket_reserve_num = 0, allocator = frame_allocator() )
|
||||
verify( alloc_error == .None, "Failed to allocate frame slab" )
|
||||
}
|
||||
|
||||
context.allocator = frame_allocator()
|
||||
context.temp_allocator = transient_allocator()
|
||||
|
||||
rl.PollInputEvents()
|
||||
|
||||
should_close = update( host_delta_time )
|
||||
render()
|
||||
|
||||
rl.SwapScreenBuffer()
|
||||
}
|
||||
|
||||
context.allocator = frame_allocator()
|
||||
context.temp_allocator = transient_allocator()
|
||||
|
||||
rl.PollInputEvents()
|
||||
|
||||
result := update( host_delta_time )
|
||||
render()
|
||||
|
||||
rl.SwapScreenBuffer()
|
||||
|
||||
config.engine_refresh_hz = uint(monitor_refresh_hz)
|
||||
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( frametime_delta_ns )
|
||||
frametime_elapsed_ms = frametime_delta_ms + host_delta_time
|
||||
|
||||
if frametime_elapsed_ms < frametime_target_ms
|
||||
// Timing
|
||||
{
|
||||
sleep_ms := frametime_target_ms - frametime_elapsed_ms
|
||||
pre_sleep_tick := time.tick_now()
|
||||
// profile("Client tick timing processing")
|
||||
config.engine_refresh_hz = uint(monitor_refresh_hz)
|
||||
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
|
||||
|
||||
if sleep_ms > 0 {
|
||||
thread_sleep( cast(Duration) sleep_ms * MS_To_NS )
|
||||
// thread__highres_wait( sleep_ms )
|
||||
}
|
||||
frametime_delta_ns = time.tick_lap_time( & client_tick )
|
||||
frametime_delta_ms = duration_ms( frametime_delta_ns )
|
||||
frametime_delta_seconds = duration_seconds( frametime_delta_ns )
|
||||
frametime_elapsed_ms = frametime_delta_ms + host_delta_time
|
||||
|
||||
sleep_delta_ns := time.tick_lap_time( & pre_sleep_tick)
|
||||
sleep_delta_ms := duration_ms( sleep_delta_ns )
|
||||
if frametime_elapsed_ms < frametime_target_ms
|
||||
{
|
||||
sleep_ms := frametime_target_ms - frametime_elapsed_ms
|
||||
pre_sleep_tick := time.tick_now()
|
||||
|
||||
if sleep_delta_ms < sleep_ms {
|
||||
// log( str_fmt_tmp("frametime sleep was off by: %v ms", sleep_delta_ms - sleep_ms ))
|
||||
}
|
||||
if sleep_ms > 0 {
|
||||
thread_sleep( cast(Duration) sleep_ms * MS_To_NS )
|
||||
// thread__highres_wait( 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 )
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
if frametime_elapsed_ms > 60.0 {
|
||||
log( str_fmt_tmp("Big tick! %v ms", frametime_elapsed_ms), LogLevel.Warning )
|
||||
}
|
||||
}
|
||||
|
||||
if frametime_elapsed_ms > 60.0 {
|
||||
log( str_fmt_tmp("Big tick! %v ms", frametime_elapsed_ms), LogLevel.Warning )
|
||||
}
|
||||
|
||||
return result
|
||||
return should_close
|
||||
}
|
||||
|
||||
@export
|
||||
clean_frame :: proc()
|
||||
{
|
||||
// profile( #procedure)
|
||||
state := get_state(); using state
|
||||
context.logger = to_odin_logger( & Memory_App.logger )
|
||||
|
||||
|
@ -44,8 +44,9 @@ Memory :: struct {
|
||||
// Not for large memory env states
|
||||
snapshot : MemorySnapshot,
|
||||
|
||||
replay : ReplayState,
|
||||
logger : Logger,
|
||||
replay : ReplayState,
|
||||
logger : Logger,
|
||||
profiler : ^SpallProfiler
|
||||
}
|
||||
|
||||
persistent_allocator :: proc() -> Allocator {
|
||||
|
@ -8,7 +8,9 @@ import "core:os"
|
||||
|
||||
import rl "vendor:raylib"
|
||||
|
||||
Font_Largest_Px_Size :: 96
|
||||
Font_Largest_Px_Size :: 32
|
||||
|
||||
Font_Size_Interval :: 2
|
||||
|
||||
// Font_Default :: ""
|
||||
Font_Default :: FontID { 0, "" }
|
||||
@ -52,7 +54,7 @@ FontDef :: struct {
|
||||
// data : []u8,
|
||||
|
||||
default_size : i32,
|
||||
size_table : [Font_Largest_Px_Size / 2] FontGlyphsRender,
|
||||
size_table : [Font_Largest_Px_Size / Font_Size_Interval] FontGlyphsRender,
|
||||
}
|
||||
|
||||
FontProviderData :: struct {
|
||||
@ -61,6 +63,7 @@ FontProviderData :: struct {
|
||||
|
||||
font_provider_startup :: proc()
|
||||
{
|
||||
profile(#procedure)
|
||||
state := get_state()
|
||||
font_provider_data := & get_state().font_provider_data; using font_provider_data
|
||||
|
||||
@ -94,6 +97,7 @@ font_load :: proc( path_file : string,
|
||||
desired_id : string = Font_Load_Gen_ID
|
||||
) -> FontID
|
||||
{
|
||||
profile(#procedure)
|
||||
font_provider_data := & get_state().font_provider_data; using font_provider_data
|
||||
|
||||
font_data, read_succeded : = os.read_entire_file( path_file, context.temp_allocator )
|
||||
@ -114,7 +118,7 @@ font_load :: proc( path_file : string,
|
||||
default_size = Font_Default_Point_Size
|
||||
}
|
||||
|
||||
key := cast(u64) xxh32( transmute([]byte) desired_id )
|
||||
key := cast(u64) crc32( transmute([]byte) desired_id )
|
||||
def, set_error := zpl_hmap_set( & font_cache, key,FontDef {} )
|
||||
verify( set_error == AllocatorError.None, "Failed to add new font entry to cache" )
|
||||
|
||||
@ -127,9 +131,10 @@ font_load :: proc( path_file : string,
|
||||
|
||||
// Render all sizes at once
|
||||
// Note(Ed) : We only generate textures for even multiples of the font.
|
||||
for font_size : i32 = 2; font_size <= Font_Largest_Px_Size; font_size += 2
|
||||
for font_size : i32 = Font_Size_Interval; font_size <= Font_Largest_Px_Size; font_size += Font_Size_Interval
|
||||
{
|
||||
id := (font_size / 2) + (font_size % 2)
|
||||
profile("font size render")
|
||||
id := (font_size / Font_Size_Interval) + (font_size % Font_Size_Interval)
|
||||
|
||||
px_render := & def.size_table[id - 1]
|
||||
using px_render
|
||||
@ -170,16 +175,27 @@ to_rl_Font :: proc( id : FontID, size := Font_Use_Default_Size ) -> rl.Font
|
||||
{
|
||||
font_provider_data := & get_state().font_provider_data; using font_provider_data
|
||||
|
||||
even_size := math.round(size * 0.5) * 2.0
|
||||
size := clamp( i32( even_size), 8, Font_Largest_Px_Size )
|
||||
even_size := math.round(size * (1.0/f32(Font_Size_Interval))) * f32(Font_Size_Interval)
|
||||
size := clamp( i32( even_size), 4, Font_Largest_Px_Size )
|
||||
def := zpl_hmap_get( & font_cache, id.key )
|
||||
size = size if size != i32(Font_Use_Default_Size) else def.default_size
|
||||
|
||||
id := (size / 2) + (size % 2)
|
||||
id := (size / Font_Size_Interval) + (size % Font_Size_Interval)
|
||||
px_render := & def.size_table[ id - 1 ]
|
||||
|
||||
// This is free for now perf wise... may have to move this out to on a setting change later.
|
||||
rl.SetTextureFilter( px_render.texture, rl.TextureFilter.TRILINEAR )
|
||||
if id <= 8 {
|
||||
rl.SetTextureFilter( px_render.texture, rl.TextureFilter.POINT )
|
||||
}
|
||||
else if id <= 14 {
|
||||
rl.SetTextureFilter( px_render.texture, rl.TextureFilter.POINT )
|
||||
}
|
||||
else if id <= 48 {
|
||||
rl.SetTextureFilter( px_render.texture, rl.TextureFilter.POINT )
|
||||
}
|
||||
else if id > 48 {
|
||||
rl.SetTextureFilter( px_render.texture, rl.TextureFilter.POINT )
|
||||
}
|
||||
|
||||
rl_font : rl.Font
|
||||
rl_font.baseSize = px_render.size
|
||||
|
@ -18,7 +18,7 @@ import "base:runtime"
|
||||
import c "core:c/libc"
|
||||
import "core:dynlib"
|
||||
import "core:hash"
|
||||
// crc32 :: hash.crc32
|
||||
crc32 :: hash.crc32
|
||||
import "core:hash/xxhash"
|
||||
xxh32 :: xxhash.XXH32
|
||||
import fmt_io "core:fmt"
|
||||
|
@ -70,6 +70,7 @@ array_init_reserve :: proc
|
||||
|
||||
array_append :: proc( self : ^Array( $ Type), value : Type ) -> AllocatorError
|
||||
{
|
||||
// profile(#procedure)
|
||||
if self.header.num == self.header.capacity
|
||||
{
|
||||
grow_result := array_grow( self, self.header.capacity )
|
||||
@ -206,6 +207,7 @@ array_free :: proc( using self : Array( $ Type ) ) {
|
||||
|
||||
array_grow :: proc( using self : ^Array( $ Type ), min_capacity : u64 ) -> AllocatorError
|
||||
{
|
||||
profile(#procedure)
|
||||
new_capacity := array_grow_formula( capacity )
|
||||
|
||||
if new_capacity < min_capacity {
|
||||
|
@ -84,6 +84,7 @@ zpl_hmap_destroy :: proc( using self : ^ HMapZPL( $ Type ) ) {
|
||||
|
||||
zpl_hmap_get :: proc( using self : ^ HMapZPL( $ Type ), key : u64 ) -> ^ Type
|
||||
{
|
||||
// profile(#procedure)
|
||||
id := zpl_hmap_find( self, key ).entry_index
|
||||
if id >= 0 {
|
||||
return & entries.data[id].value
|
||||
@ -113,6 +114,7 @@ zpl_hmap_grow :: proc( using self : ^ HMapZPL( $ Type ) ) -> AllocatorError {
|
||||
|
||||
zpl_hmap_rehash :: proc( ht : ^ HMapZPL( $ Type ), new_num : u64 ) -> AllocatorError
|
||||
{
|
||||
profile(#procedure)
|
||||
// For now the prototype should never allow this to happen.
|
||||
ensure( false, "ZPL HMAP IS REHASHING" )
|
||||
last_added_index : i64
|
||||
@ -183,6 +185,7 @@ zpl_hmap_remove_entry :: proc( using self : ^ HMapZPL( $ Type ), id : i64 ) {
|
||||
|
||||
zpl_hmap_set :: proc( using self : ^ HMapZPL( $ Type), key : u64, value : Type ) -> (^ Type, AllocatorError)
|
||||
{
|
||||
// profile(#procedure)
|
||||
id : i64 = 0
|
||||
find_result : HMapZPL_FindResult
|
||||
|
||||
@ -237,6 +240,7 @@ zpl_hmap_add_entry :: proc( using self : ^ HMapZPL( $ Type), key : u64 ) -> i64
|
||||
|
||||
zpl_hmap_find :: proc( using self : ^ HMapZPL( $ Type), key : u64 ) -> HMapZPL_FindResult
|
||||
{
|
||||
// profile(#procedure)
|
||||
result : HMapZPL_FindResult = { -1, -1, -1 }
|
||||
|
||||
if hashes.num > 0 {
|
||||
|
@ -91,6 +91,7 @@ pool_destroy :: proc ( using self : Pool )
|
||||
|
||||
pool_allocate_buckets :: proc( using self : Pool, num_buckets : uint ) -> AllocatorError
|
||||
{
|
||||
profile(#procedure)
|
||||
if num_buckets == 0 {
|
||||
return .Invalid_Argument
|
||||
}
|
||||
@ -124,6 +125,7 @@ pool_allocate_buckets :: proc( using self : Pool, num_buckets : uint ) -> Alloca
|
||||
|
||||
pool_grab :: proc( using pool : Pool ) -> ( block : []byte, alloc_error : AllocatorError )
|
||||
{
|
||||
// profile(#procedure)
|
||||
alloc_error = .None
|
||||
|
||||
// Check the free-list first for a block
|
||||
@ -189,6 +191,7 @@ pool_grab :: proc( using pool : Pool ) -> ( block : []byte, alloc_error : Alloca
|
||||
|
||||
pool_release :: proc( self : Pool, block : []byte, loc := #caller_location )
|
||||
{
|
||||
// profile(#procedure)
|
||||
if Pool_Check_Release_Object_Validity {
|
||||
within_bucket := pool_validate_ownership( self, block )
|
||||
verify( within_bucket, "Attempted to release data that is not within a bucket of this pool", location = loc )
|
||||
@ -221,6 +224,7 @@ pool_reset :: proc( using pool : Pool )
|
||||
|
||||
pool_validate_ownership :: proc( using self : Pool, block : [] byte ) -> b32
|
||||
{
|
||||
profile(#procedure)
|
||||
within_bucket := b32(false)
|
||||
|
||||
// Compiler Bug : Same as pool_reset
|
||||
|
23
code/grime_profiler.odin
Normal file
23
code/grime_profiler.odin
Normal file
@ -0,0 +1,23 @@
|
||||
package sectr
|
||||
|
||||
import "base:runtime"
|
||||
import "core:prof/spall"
|
||||
|
||||
SpallProfiler :: struct {
|
||||
ctx : spall.Context,
|
||||
buffer : spall.Buffer,
|
||||
}
|
||||
|
||||
@(deferred_none=profile_end)
|
||||
profile :: #force_inline proc "contextless" ( name : string, loc := #caller_location ) {
|
||||
spall._buffer_begin( & Memory_App.profiler.ctx, & Memory_App.profiler.buffer, name, "", loc )
|
||||
}
|
||||
|
||||
profile_begin :: #force_inline proc "contextless" ( name : string, loc := #caller_location ) {
|
||||
spall._buffer_begin( & Memory_App.profiler.ctx, & Memory_App.profiler.buffer, name, "", loc )
|
||||
}
|
||||
|
||||
profile_end :: #force_inline proc "contextless" ()
|
||||
{
|
||||
spall._buffer_end( & Memory_App.profiler.ctx, & Memory_App.profiler.buffer)
|
||||
}
|
@ -103,6 +103,7 @@ slab_alloc :: proc( using self : Slab,
|
||||
loc := #caller_location
|
||||
) -> ( data : []byte, alloc_error : AllocatorError )
|
||||
{
|
||||
// profile(#procedure)
|
||||
pool : Pool
|
||||
for id in 0 ..< pools.idx {
|
||||
pool = pools.items[id]
|
||||
@ -130,6 +131,7 @@ slab_alloc :: proc( using self : Slab,
|
||||
|
||||
slab_free :: proc( using self : Slab, data : []byte, loc := #caller_location )
|
||||
{
|
||||
// profile(#procedure)
|
||||
pool : Pool
|
||||
for id in 0 ..< pools.idx
|
||||
{
|
||||
@ -150,6 +152,7 @@ slab_resize :: proc( using self : Slab,
|
||||
loc := #caller_location
|
||||
) -> ( new_data : []byte, alloc_error : AllocatorError )
|
||||
{
|
||||
// profile(#procedure)
|
||||
old_size := uint( len(data))
|
||||
|
||||
pool_resize, pool_old : Pool
|
||||
|
@ -62,30 +62,35 @@ str_intern :: proc(
|
||||
content : string
|
||||
) -> StringCached
|
||||
{
|
||||
// profile(#procedure)
|
||||
cache := get_state().string_cache
|
||||
|
||||
key := u64( xxh32( transmute([]byte) content ))
|
||||
key := u64( crc32( transmute([]byte) content ))
|
||||
result := zpl_hmap_get( & cache.table, key )
|
||||
if result != nil {
|
||||
return (result ^)
|
||||
}
|
||||
|
||||
length := len(content)
|
||||
// str_mem, alloc_error := alloc( length, mem.DEFAULT_ALIGNMENT )
|
||||
str_mem, alloc_error := slab_alloc( cache.slab, uint(length), uint(mem.DEFAULT_ALIGNMENT) )
|
||||
verify( alloc_error == .None, "String cache had a backing allocator error" )
|
||||
// profile_begin("new entry")
|
||||
{
|
||||
length := len(content)
|
||||
// str_mem, alloc_error := alloc( length, mem.DEFAULT_ALIGNMENT )
|
||||
str_mem, alloc_error := slab_alloc( cache.slab, uint(length), uint(mem.DEFAULT_ALIGNMENT) )
|
||||
verify( alloc_error == .None, "String cache had a backing allocator error" )
|
||||
|
||||
// copy_non_overlapping( str_mem, raw_data(content), length )
|
||||
copy_non_overlapping( raw_data(str_mem), raw_data(content), length )
|
||||
// copy_non_overlapping( str_mem, raw_data(content), length )
|
||||
copy_non_overlapping( raw_data(str_mem), raw_data(content), length )
|
||||
|
||||
runes : []rune
|
||||
// runes, alloc_error = to_runes( content, persistent_allocator() )
|
||||
runes, alloc_error = to_runes( content, slab_allocator(cache.slab) )
|
||||
verify( alloc_error == .None, "String cache had a backing allocator error" )
|
||||
runes : []rune
|
||||
// runes, alloc_error = to_runes( content, persistent_allocator() )
|
||||
runes, alloc_error = to_runes( content, slab_allocator(cache.slab) )
|
||||
verify( alloc_error == .None, "String cache had a backing allocator error" )
|
||||
|
||||
// result, alloc_error = zpl_hmap_set( & cache.table, key, StringCached { transmute(string) byte_slice(str_mem, length), runes } )
|
||||
result, alloc_error = zpl_hmap_set( & cache.table, key, StringCached { transmute(string) str_mem, runes } )
|
||||
verify( alloc_error == .None, "String cache had a backing allocator error" )
|
||||
// result, alloc_error = zpl_hmap_set( & cache.table, key, StringCached { transmute(string) byte_slice(str_mem, length), runes } )
|
||||
result, alloc_error = zpl_hmap_set( & cache.table, key, StringCached { transmute(string) str_mem, runes } )
|
||||
verify( alloc_error == .None, "String cache had a backing allocator error" )
|
||||
}
|
||||
// profile_end()
|
||||
|
||||
return (result ^)
|
||||
}
|
||||
|
@ -132,6 +132,7 @@ varena_alloc :: proc( using self : ^VArena,
|
||||
needs_more_committed := commit_left < size_to_allocate
|
||||
if needs_more_committed
|
||||
{
|
||||
profile("VArena Growing")
|
||||
next_commit_size := growth_policy( commit_used, committed, reserved, size_to_allocate )
|
||||
alloc_error = virtual_commit( vmem, next_commit_size )
|
||||
if alloc_error != .None {
|
||||
|
@ -59,6 +59,7 @@ import "core:time"
|
||||
Duration :: time.Duration
|
||||
duration_seconds :: time.duration_seconds
|
||||
thread_sleep :: time.sleep
|
||||
import "core:prof/spall"
|
||||
import rl "vendor:raylib"
|
||||
import sectr "../."
|
||||
VArena :: sectr.VArena
|
||||
@ -70,6 +71,7 @@ import sectr "../."
|
||||
logger_init :: sectr.logger_init
|
||||
LogLevel :: sectr.LogLevel
|
||||
log :: sectr.log
|
||||
SpallProfiler :: sectr.SpallProfiler
|
||||
to_odin_logger :: sectr.to_odin_logger
|
||||
TrackedAllocator :: sectr.TrackedAllocator
|
||||
tracked_allocator :: sectr.tracked_allocator
|
||||
@ -108,8 +110,9 @@ ClientMemory :: struct {
|
||||
files_buffer : VArena,
|
||||
}
|
||||
|
||||
setup_memory :: proc() -> ClientMemory
|
||||
setup_memory :: proc( profiler : ^SpallProfiler ) -> ClientMemory
|
||||
{
|
||||
spall.SCOPED_EVENT( & profiler.ctx, & profiler.buffer, #procedure )
|
||||
memory : ClientMemory; using memory
|
||||
|
||||
// Setup the static arena for the entire application
|
||||
@ -211,8 +214,10 @@ unload_sectr_api :: proc( module : ^ sectr.ModuleAPI )
|
||||
log("Unloaded sectr API")
|
||||
}
|
||||
|
||||
sync_sectr_api :: proc( sectr_api : ^sectr.ModuleAPI, memory : ^ClientMemory, logger : ^Logger )
|
||||
sync_sectr_api :: proc( sectr_api : ^sectr.ModuleAPI, memory : ^ClientMemory, logger : ^Logger, profiler : ^SpallProfiler )
|
||||
{
|
||||
spall.SCOPED_EVENT( & profiler.ctx, & profiler.buffer, #procedure )
|
||||
|
||||
if write_time, result := os.last_write_time_by_name( Path_Sectr_Module );
|
||||
result == os.ERROR_NONE && sectr_api.write_time != write_time
|
||||
{
|
||||
@ -223,10 +228,11 @@ sync_sectr_api :: proc( sectr_api : ^sectr.ModuleAPI, memory : ^ClientMemory, lo
|
||||
for ; file_is_locked( Path_Sectr_Debug_Symbols ) && file_is_locked( Path_Sectr_Live_Module ); {}
|
||||
thread_sleep( Millisecond * 100 )
|
||||
|
||||
sectr_api ^ = load_sectr_api( version_id )
|
||||
(sectr_api ^) = load_sectr_api( version_id )
|
||||
verify( sectr_api.lib_version != 0, "Failed to hot-reload the sectr module" )
|
||||
|
||||
sectr_api.reload(
|
||||
profiler,
|
||||
& memory.persistent,
|
||||
& memory.frame,
|
||||
& memory.transient,
|
||||
@ -240,6 +246,15 @@ main :: proc()
|
||||
state : RuntimeState
|
||||
using state
|
||||
|
||||
// Setup profiling
|
||||
profiler : SpallProfiler
|
||||
{
|
||||
buffer_backing := make([]u8, spall.BUFFER_DEFAULT_SIZE)
|
||||
profiler.ctx = spall.context_create("sectr.spall")
|
||||
profiler.buffer = spall.buffer_create(buffer_backing)
|
||||
}
|
||||
spall.SCOPED_EVENT( & profiler.ctx, & profiler.buffer, #procedure )
|
||||
|
||||
// Generating the logger's name, it will be used when the app is shutting down.
|
||||
path_logger_finalized : string
|
||||
{
|
||||
@ -268,7 +283,7 @@ main :: proc()
|
||||
log( to_str(builder) )
|
||||
}
|
||||
|
||||
memory := setup_memory()
|
||||
memory := setup_memory( & profiler )
|
||||
|
||||
// TODO(Ed): Cannot use the manually created allocators for the host. Not sure why
|
||||
// Something is wrong with the tracked_allocator init
|
||||
@ -286,6 +301,7 @@ main :: proc()
|
||||
running = true;
|
||||
sectr_api = sectr_api
|
||||
sectr_api.startup(
|
||||
& profiler,
|
||||
& memory.persistent,
|
||||
& memory.frame,
|
||||
& memory.transient,
|
||||
@ -299,8 +315,10 @@ main :: proc()
|
||||
// TODO(Ed) : This should have an end status so that we know the reason the engine stopped.
|
||||
for ; running ;
|
||||
{
|
||||
spall.SCOPED_EVENT( & profiler.ctx, & profiler.buffer, "Host Tick" )
|
||||
|
||||
// Hot-Reload
|
||||
sync_sectr_api( & sectr_api, & memory, & logger )
|
||||
sync_sectr_api( & sectr_api, & memory, & logger, & profiler )
|
||||
|
||||
running = sectr_api.tick( duration_seconds( delta_ns ), delta_ns )
|
||||
sectr_api.clean_frame()
|
||||
@ -318,6 +336,9 @@ main :: proc()
|
||||
sectr_api.shutdown()
|
||||
unload_sectr_api( & sectr_api )
|
||||
|
||||
spall.buffer_destroy( & profiler.ctx, & profiler.buffer )
|
||||
spall.context_destroy( & profiler.ctx )
|
||||
|
||||
log("Succesfuly closed")
|
||||
file_close( logger.file )
|
||||
// TODO(Ed) : Add string interning!!!!!!!!!
|
||||
|
@ -290,6 +290,7 @@ import rl "vendor:raylib"
|
||||
|
||||
poll_input :: proc( old, new : ^ InputState )
|
||||
{
|
||||
profile(#procedure)
|
||||
input_process_digital_btn :: proc( old_state, new_state : ^ DigitalBtn, is_down : b32 )
|
||||
{
|
||||
new_state.ended_down = is_down
|
||||
@ -304,6 +305,7 @@ poll_input :: proc( old, new : ^ InputState )
|
||||
|
||||
// Keyboard
|
||||
{
|
||||
// profile("Keyboard")
|
||||
check_range :: proc( old, new : ^ InputState, start, end : i32 )
|
||||
{
|
||||
for id := start; id < end; id += 1
|
||||
@ -332,6 +334,7 @@ poll_input :: proc( old, new : ^ InputState )
|
||||
|
||||
// Mouse
|
||||
{
|
||||
// profile("Mouse")
|
||||
// Process Buttons
|
||||
for id : i32 = 0; id < i32(MouseBtn.count); id += 1
|
||||
{
|
||||
|
@ -49,3 +49,12 @@ add_range2 :: #force_inline proc "contextless" ( a, b : Range2 ) -> Range2 {
|
||||
}}
|
||||
return result
|
||||
}
|
||||
|
||||
equal_range2 :: #force_inline proc "contextless" ( a, b : Range2 ) -> b32 {
|
||||
result := a.p0 == b.p0 && a.p1 == b.p1
|
||||
return b32(result)
|
||||
}
|
||||
|
||||
size_range2 :: #force_inline proc "contextless" ( value : Range2 ) -> Vec2 {
|
||||
return { value.p1.x - value.p0.x, value.p0.y - value.p1.y }
|
||||
}
|
@ -118,6 +118,7 @@ PWS_LexerData :: struct {
|
||||
|
||||
pws_parser_lex :: proc ( text : string, allocator : Allocator ) -> ( PWS_LexResult, AllocatorError )
|
||||
{
|
||||
profile(#procedure)
|
||||
using lexer : PWS_LexerData
|
||||
context.user_ptr = & lexer
|
||||
content = text
|
||||
@ -234,6 +235,7 @@ PWS_ParseData :: struct {
|
||||
|
||||
pws_parser_parse :: proc( text : string, allocator : Allocator ) -> ( PWS_ParseResult, AllocatorError )
|
||||
{
|
||||
profile(#procedure)
|
||||
using parser : PWS_ParseData
|
||||
context.user_ptr = & result
|
||||
|
||||
|
@ -6,6 +6,7 @@ import rl "vendor:raylib"
|
||||
|
||||
debug_draw_text :: proc( content : string, pos : Vec2, size : f32, color : rl.Color = rl.WHITE, font : FontID = Font_Default )
|
||||
{
|
||||
// profile(#procedure)
|
||||
state := get_state(); using state
|
||||
|
||||
if len( content ) == 0 {
|
||||
@ -35,6 +36,7 @@ debug_draw_text :: proc( content : string, pos : Vec2, size : f32, color : rl.Co
|
||||
|
||||
draw_text_string :: proc( content : string, pos : Vec2, size : f32, color : rl.Color = rl.WHITE, font : FontID = Font_Default )
|
||||
{
|
||||
// profile(#procedure)
|
||||
state := get_state(); using state
|
||||
|
||||
if len( content ) == 0 {
|
||||
@ -62,7 +64,9 @@ draw_text_string :: proc( content : string, pos : Vec2, size : f32, color : rl.C
|
||||
tint = color );
|
||||
}
|
||||
|
||||
draw_text_string_cached :: proc( content : StringCached, pos : Vec2, size : f32, color : rl.Color = rl.WHITE, font : FontID = Font_Default ) {
|
||||
draw_text_string_cached :: proc( content : StringCached, pos : Vec2, size : f32, color : rl.Color = rl.WHITE, font : FontID = Font_Default )
|
||||
{
|
||||
// profile(#procedure)
|
||||
state := get_state(); using state
|
||||
|
||||
if len( content.str ) == 0 {
|
||||
@ -91,6 +95,7 @@ draw_text_string_cached :: proc( content : StringCached, pos : Vec2, size : f32,
|
||||
// So this is a 1:1 copy except it takes Odin strings
|
||||
measure_text_size :: proc( text : string, font : FontID, font_size := Font_Use_Default_Size, spacing : f32 ) -> Vec2
|
||||
{
|
||||
// profile(#procedure)
|
||||
px_size := math.round( points_to_pixels( font_size ) )
|
||||
rl_font := to_rl_Font( font, font_size )
|
||||
|
||||
|
@ -6,6 +6,7 @@ import rl "vendor:raylib"
|
||||
|
||||
render :: proc()
|
||||
{
|
||||
profile(#procedure)
|
||||
state := get_state(); using state
|
||||
replay := & Memory_App.replay
|
||||
cam := & project.workspace.cam
|
||||
@ -21,6 +22,7 @@ render :: proc()
|
||||
render_mode_2d()
|
||||
//region Render Screenspace
|
||||
{
|
||||
profile("Render Screenspace")
|
||||
fps_msg := str_fmt_tmp( "FPS: %f", 1 / (frametime_elapsed_ms * MS_To_S) )
|
||||
fps_msg_width := measure_text_size( fps_msg, default_font, 16.0, 0.0 ).x
|
||||
fps_msg_pos := screen_get_corners().top_right - { fps_msg_width, 0 }
|
||||
@ -95,6 +97,7 @@ render :: proc()
|
||||
|
||||
render_mode_2d :: proc()
|
||||
{
|
||||
profile(#procedure)
|
||||
state := get_state(); using state
|
||||
cam := & project.workspace.cam
|
||||
|
||||
@ -108,6 +111,7 @@ render_mode_2d :: proc()
|
||||
|
||||
ImguiRender:
|
||||
{
|
||||
profile("Imgui Render")
|
||||
ui := & state.project.workspace.ui
|
||||
root := ui.root
|
||||
if root.num_children == 0 {
|
||||
@ -115,7 +119,9 @@ render_mode_2d :: proc()
|
||||
}
|
||||
|
||||
current := root.first
|
||||
for ; current != nil; {
|
||||
for ; current != nil;
|
||||
{
|
||||
profile("Box")
|
||||
parent := current.parent
|
||||
|
||||
style := current.style
|
||||
@ -125,6 +131,7 @@ render_mode_2d :: proc()
|
||||
|
||||
// TODO(Ed) : Render Borders
|
||||
|
||||
// profile_begin("Calculating Raylib rectangles")
|
||||
render_bounds := Range2 { pts = {
|
||||
world_to_screen_pos(computed.bounds.min),
|
||||
world_to_screen_pos(computed.bounds.max),
|
||||
@ -157,15 +164,47 @@ render_mode_2d :: proc()
|
||||
render_content.max.x - render_content.min.x,
|
||||
render_content.max.y - render_content.min.y,
|
||||
}
|
||||
// profile_end()
|
||||
|
||||
rl.DrawRectangleRounded( rect_bounds, style.layout.corner_radii[0], 9, style.bg_color )
|
||||
draw_rectangle :: #force_inline proc "contextless" ( rect : rl.Rectangle, style : UI_Style ) {
|
||||
if style.layout.corner_radii[0] > 0 {
|
||||
rl.DrawRectangleRounded( rect, style.layout.corner_radii[0], 9, style.bg_color )
|
||||
}
|
||||
else {
|
||||
rl.DrawRectangleRec( rect, style.bg_color )
|
||||
}
|
||||
}
|
||||
|
||||
draw_rectangle_lines :: #force_inline proc "contextless" ( rect : rl.Rectangle, style : UI_Style, color : Color, thickness : f32 ) {
|
||||
if style.layout.corner_radii[0] > 0 {
|
||||
rl.DrawRectangleRoundedLines( rect, style.layout.corner_radii[0], 9, thickness, color )
|
||||
}
|
||||
else {
|
||||
rl.DrawRectangleLinesEx( rect, thickness, color )
|
||||
}
|
||||
}
|
||||
|
||||
// profile_begin("rl.DrawRectangleRounded( rect_bounds, style.layout.corner_radii[0], 9, style.bg_color )")
|
||||
if style.bg_color.a != 0
|
||||
{
|
||||
draw_rectangle( rect_bounds, style )
|
||||
}
|
||||
// profile_end()
|
||||
|
||||
line_thickness := 1 * cam_zoom_ratio
|
||||
|
||||
rl.DrawRectangleRoundedLines( rect_padding, style.layout.corner_radii[0], 9, line_thickness, Color_Debug_UI_Padding_Bounds )
|
||||
rl.DrawRectangleRoundedLines( rect_content, style.layout.corner_radii[0], 9, line_thickness, Color_Debug_UI_Content_Bounds )
|
||||
if .Mouse_Resizable in current.flags
|
||||
// profile_begin("rl.DrawRectangleRoundedLines: padding & content")
|
||||
if equal_range2(computed.content, computed.padding) {
|
||||
// draw_rectangle_lines( rect_padding, style, Color_Debug_UI_Padding_Bounds, line_thickness )
|
||||
}
|
||||
else {
|
||||
// draw_rectangle_lines( rect_padding, style, Color_Debug_UI_Content_Bounds, line_thickness )
|
||||
}
|
||||
// profile_end()
|
||||
|
||||
if .Mouse_Resizable in current.flags && false
|
||||
{
|
||||
// profile("Resize Bounds")
|
||||
resize_border_width := cast(f32) get_state().config.ui_resize_border_width
|
||||
resize_percent_width := style.size * (resize_border_width * 1.0/ 200.0)
|
||||
resize_border_non_range := add(current.computed.bounds, range2(
|
||||
@ -182,12 +221,16 @@ render_mode_2d :: proc()
|
||||
render_resize.max.x - render_resize.min.x,
|
||||
render_resize.max.y - render_resize.min.y,
|
||||
}
|
||||
rl.DrawRectangleRoundedLines( rect_resize, style.layout.corner_radii[0], 9, line_thickness, Color_Red )
|
||||
// rl.DrawRectangleRoundedLines( rect_resize, style.layout.corner_radii[0], 9, line_thickness, Color_Red )
|
||||
draw_rectangle_lines( rect_padding, style, Color_Red, line_thickness )
|
||||
}
|
||||
|
||||
point_radius := 3 * cam_zoom_ratio
|
||||
rl.DrawCircleV( render_bounds.p0, point_radius, Color_Red )
|
||||
rl.DrawCircleV( render_bounds.p1, point_radius, Color_Blue )
|
||||
point_radius := 2 * cam_zoom_ratio
|
||||
|
||||
// profile_begin("circles")
|
||||
// rl.DrawCircleV( render_bounds.p0, point_radius, Color_Red )
|
||||
// rl.DrawCircleV( render_bounds.p1, point_radius, Color_Blue )
|
||||
// profile_end()
|
||||
|
||||
if len(current.text.str) > 0 {
|
||||
draw_text_string_cached( current.text, world_to_screen_pos(computed.text_pos), style.font_size, style.text_color )
|
||||
|
@ -29,6 +29,7 @@ DebugActions :: struct {
|
||||
|
||||
poll_debug_actions :: proc( actions : ^ DebugActions, input : ^ InputState )
|
||||
{
|
||||
// profile(#procedure)
|
||||
using actions
|
||||
using input
|
||||
|
||||
@ -64,6 +65,7 @@ frametime_delta32 :: #force_inline proc "contextless" () -> f32 {
|
||||
|
||||
update :: proc( delta_time : f64 ) -> b32
|
||||
{
|
||||
profile(#procedure)
|
||||
state := get_state(); using state
|
||||
replay := & Memory_App.replay
|
||||
workspace := & project.workspace
|
||||
@ -146,6 +148,7 @@ update :: proc( delta_time : f64 ) -> b32
|
||||
|
||||
//region Camera Manual Nav
|
||||
{
|
||||
// profile("Camera Manual Nav")
|
||||
digital_move_speed : f32 = 200.0
|
||||
|
||||
if workspace.zoom_target == 0.0 {
|
||||
@ -190,6 +193,8 @@ update :: proc( delta_time : f64 ) -> b32
|
||||
|
||||
//region Imgui Tick
|
||||
{
|
||||
profile("Imgui Tick")
|
||||
|
||||
// Creates the root box node, set its as the first parent.
|
||||
ui_graph_build( & state.project.workspace.ui )
|
||||
ui := ui_context
|
||||
@ -236,17 +241,27 @@ update :: proc( delta_time : f64 ) -> b32
|
||||
// Whitespace AST test
|
||||
when true
|
||||
{
|
||||
profile("Whitespace AST test")
|
||||
|
||||
text_style := frame_style_default
|
||||
text_style.flags = {
|
||||
.Fixed_Position_X, .Fixed_Position_Y,
|
||||
// .Fixed_Width, .Fixed_Height,
|
||||
}
|
||||
|
||||
text_theme := UI_StyleTheme { styles = {
|
||||
frame_style_default,
|
||||
frame_style_default,
|
||||
frame_style_default,
|
||||
frame_style_default,
|
||||
text_style,
|
||||
text_style,
|
||||
text_style,
|
||||
text_style,
|
||||
}}
|
||||
text_theme.default.bg_color = Color_Transparent
|
||||
text_theme.disabled.bg_color = Color_Frame_Disabled
|
||||
text_theme.hovered.bg_color = Color_Frame_Hover
|
||||
text_theme.focused.bg_color = Color_Frame_Select
|
||||
|
||||
layout_text := default_layout
|
||||
|
||||
ui_style_theme( text_theme )
|
||||
|
||||
alloc_error : AllocatorError; success : bool
|
||||
@ -267,6 +282,8 @@ update :: proc( delta_time : f64 ) -> b32
|
||||
|
||||
for line in array_to_slice_num( debug.lorem_parse.lines )
|
||||
{
|
||||
profile("WS AST Line")
|
||||
|
||||
head := line.first
|
||||
for ; head != nil;
|
||||
{
|
||||
@ -282,32 +299,30 @@ update :: proc( delta_time : f64 ) -> b32
|
||||
widget = ui_text( label.str, head.content )
|
||||
label_id += 1
|
||||
|
||||
layout_text.pos.x += widget.style.layout.size.x
|
||||
layout_text.pos.x += size_range2( widget.computed.bounds ).x
|
||||
|
||||
case .Spaces:
|
||||
label := str_intern( str_fmt_alloc( "%v %v", "space", label_id ))
|
||||
// widget = ui_text( label.str, text_space, {} )
|
||||
// widget.style.layout.size = Vec2 { 1, 16 }
|
||||
widget = ui_space( label.str )
|
||||
label_id += 1
|
||||
|
||||
for idx in 1 ..< len( head.content.runes )
|
||||
{
|
||||
widget.style.layout.size.x += widget.style.layout.size.x
|
||||
// TODO(Ed): VIRTUAL WHITESPACE
|
||||
// widget.style.layout.size.x += range2_size( widget.computed.bounds )
|
||||
}
|
||||
layout_text.pos.x += widget.style.layout.size.x
|
||||
layout_text.pos.x += size_range2( widget.computed.bounds ).x
|
||||
|
||||
case .Tabs:
|
||||
label := str_intern( str_fmt_alloc( "%v %v", "tab", label_id ))
|
||||
// widget = ui_text( label.str, text_tab, {} )
|
||||
widget = ui_tab( label.str )
|
||||
label_id += 1
|
||||
|
||||
for idx in 1 ..< len( head.content.runes )
|
||||
{
|
||||
widget.style.layout.size.x += widget.style.layout.size.x
|
||||
// widget.style.layout.size.x += range2_size( widget.computed.bounds )
|
||||
}
|
||||
layout_text.pos.x += widget.style.layout.size.x
|
||||
layout_text.pos.x += size_range2( widget.computed.bounds ).x
|
||||
}
|
||||
|
||||
array_append( widgets_ptr, widget )
|
||||
|
38
code/ui.odin
38
code/ui.odin
@ -144,12 +144,14 @@ UI_Layout :: struct {
|
||||
pos : Vec2,
|
||||
// TODO(Ed) : Should everything no matter what its parent is use a WS_Pos instead of a raw vector pos?
|
||||
|
||||
size : Vec2,
|
||||
|
||||
// If the box is a child of the root parent, its automatically in world space and thus will use the tile_pos.
|
||||
tile_pos : WS_Pos,
|
||||
|
||||
// TODO(Ed) : Add support for size_to_content?
|
||||
// size_to_content : b32,
|
||||
size : Vec2,
|
||||
size_to_text : b8,
|
||||
// size_to_content : b8,
|
||||
}
|
||||
|
||||
UI_Signal :: struct {
|
||||
@ -341,6 +343,8 @@ ui_box_from_key :: proc( cache : ^HMapZPL(UI_Box), key : UI_Key ) -> (^UI_Box) {
|
||||
|
||||
ui_box_make :: proc( flags : UI_BoxFlags, label : string ) -> (^ UI_Box)
|
||||
{
|
||||
// profile(#procedure)
|
||||
|
||||
using ui := get_state().ui_context
|
||||
|
||||
key := ui_key_from_string( label )
|
||||
@ -348,6 +352,8 @@ ui_box_make :: proc( flags : UI_BoxFlags, label : string ) -> (^ UI_Box)
|
||||
curr_box : (^ UI_Box)
|
||||
prev_box := zpl_hmap_get( prev_cache, cast(u64) key )
|
||||
{
|
||||
// profile("Assigning current box")
|
||||
|
||||
set_result : ^ UI_Box
|
||||
set_error : AllocatorError
|
||||
if prev_box != nil {
|
||||
@ -388,7 +394,8 @@ ui_box_make :: proc( flags : UI_BoxFlags, label : string ) -> (^ UI_Box)
|
||||
return curr_box
|
||||
}
|
||||
|
||||
ui_box_tranverse_next :: proc( box : ^ UI_Box ) -> (^ UI_Box) {
|
||||
ui_box_tranverse_next :: #force_inline proc "contextless" ( box : ^ UI_Box ) -> (^ UI_Box)
|
||||
{
|
||||
// If current has children, do them first
|
||||
if box.first != nil {
|
||||
return box.first
|
||||
@ -423,6 +430,8 @@ ui_drag_delta :: #force_inline proc "contextless" () -> Vec2 {
|
||||
|
||||
ui_graph_build_begin :: proc( ui : ^ UI_State, bounds : Vec2 = {} )
|
||||
{
|
||||
profile(#procedure)
|
||||
|
||||
get_state().ui_context = ui
|
||||
using get_state().ui_context
|
||||
|
||||
@ -444,6 +453,8 @@ ui_graph_build_begin :: proc( ui : ^ UI_State, bounds : Vec2 = {} )
|
||||
// TODO(Ed) :: Is this even needed?
|
||||
ui_graph_build_end :: proc()
|
||||
{
|
||||
profile(#procedure)
|
||||
|
||||
ui_parent_pop() // Should be ui_context.root
|
||||
|
||||
// Regenerate the computed layout if dirty
|
||||
@ -457,8 +468,25 @@ ui_graph_build :: proc( ui : ^ UI_State ) {
|
||||
ui_graph_build_begin( ui )
|
||||
}
|
||||
|
||||
ui_key_from_string :: proc( value : string ) -> UI_Key {
|
||||
key := cast(UI_Key) xxh32( transmute([]byte) value )
|
||||
ui_key_from_string :: proc( value : string ) -> UI_Key
|
||||
{
|
||||
// profile(#procedure)
|
||||
USE_RAD_DEBUGGERS_METHOD :: true
|
||||
|
||||
key : UI_Key
|
||||
|
||||
when USE_RAD_DEBUGGERS_METHOD {
|
||||
hash : u64
|
||||
for str_byte in transmute([]byte) value {
|
||||
hash = ((hash << 5) + hash) + u64(str_byte)
|
||||
}
|
||||
key = cast(UI_Key) hash
|
||||
}
|
||||
|
||||
when ! USE_RAD_DEBUGGERS_METHOD {
|
||||
key = cast(UI_Key) crc32( transmute([]byte) value )
|
||||
}
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ package sectr
|
||||
|
||||
ui_compute_layout :: proc()
|
||||
{
|
||||
profile(#procedure)
|
||||
state := get_state()
|
||||
|
||||
root := state.project.workspace.ui.root
|
||||
@ -21,6 +22,7 @@ ui_compute_layout :: proc()
|
||||
current := root.first
|
||||
for ; current != nil;
|
||||
{
|
||||
profile("Layout Box")
|
||||
parent := current.parent
|
||||
parent_content := parent.computed.content
|
||||
computed := & current.computed
|
||||
@ -46,6 +48,15 @@ ui_compute_layout :: proc()
|
||||
pos.y += margins.p1.y * anchor.y1
|
||||
}
|
||||
|
||||
text_size : Vec2
|
||||
// If the computed matches, we alreayd have the size, don't bother.
|
||||
// if computed.text_size.y == style.font_size {
|
||||
if current.first_frame || ! style.size_to_text || computed.text_size.y != size_range2(computed.bounds).y {
|
||||
text_size = cast(Vec2) measure_text_size( current.text.str, style.font, style.font_size, 0 )
|
||||
} else {
|
||||
text_size = computed.text_size
|
||||
}
|
||||
|
||||
size : Vec2
|
||||
if UI_StyleFlag.Fixed_Width in style.flags {
|
||||
size.x = layout.size.x
|
||||
@ -53,6 +64,7 @@ ui_compute_layout :: proc()
|
||||
else {
|
||||
// TODO(Ed) : Not sure what todo here...
|
||||
}
|
||||
|
||||
if UI_StyleFlag.Fixed_Height in style.flags {
|
||||
size.y = layout.size.y
|
||||
}
|
||||
@ -60,6 +72,10 @@ ui_compute_layout :: proc()
|
||||
// TODO(Ed) : Not sure what todo here...
|
||||
}
|
||||
|
||||
if style.size_to_text {
|
||||
size = text_size
|
||||
}
|
||||
|
||||
half_size := size * 0.5
|
||||
size_bounds := range2(
|
||||
Vec2 {},
|
||||
@ -94,12 +110,11 @@ ui_compute_layout :: proc()
|
||||
// Text
|
||||
if len(current.text.str) > 0
|
||||
{
|
||||
// profile("Text")
|
||||
top_left := content.p0
|
||||
bottom_right := content.p1
|
||||
|
||||
content_size := Vec2 { top_left.x - bottom_right.x, top_left.y - bottom_right.y }
|
||||
text_size := cast(Vec2) measure_text_size( current.text.str, style.font, style.font_size, 0 )
|
||||
|
||||
text_pos : Vec2
|
||||
text_pos = top_left
|
||||
text_pos.x += (-content_size.x - text_size.x) * layout.text_alignment.x
|
||||
|
@ -2,6 +2,7 @@ package sectr
|
||||
|
||||
ui_signal_from_box :: proc ( box : ^ UI_Box ) -> UI_Signal
|
||||
{
|
||||
// profile(#procedure)
|
||||
ui := get_state().ui_context
|
||||
input := get_state().input
|
||||
|
||||
@ -10,6 +11,7 @@ ui_signal_from_box :: proc ( box : ^ UI_Box ) -> UI_Signal
|
||||
signal := UI_Signal {}
|
||||
|
||||
// Cursor Collision
|
||||
// profile_begin( "Cursor collision")
|
||||
signal.cursor_pos = ui_cursor_pos()
|
||||
signal.cursor_over = cast(b8) pos_within_range2( signal.cursor_pos, box.computed.bounds )
|
||||
|
||||
@ -22,7 +24,9 @@ ui_signal_from_box :: proc ( box : ^ UI_Box ) -> UI_Signal
|
||||
within_resize_range := cast(b8) ! pos_within_range2( signal.cursor_pos, resize_border_non_range )
|
||||
within_resize_range &= signal.cursor_over
|
||||
within_resize_range &= .Mouse_Resizable in box.flags
|
||||
// profile_end()
|
||||
|
||||
// profile_begin("misc")
|
||||
left_pressed := pressed( input.mouse.left )
|
||||
left_released := released( input.mouse.left )
|
||||
|
||||
@ -118,11 +122,13 @@ ui_signal_from_box :: proc ( box : ^ UI_Box ) -> UI_Signal
|
||||
ui.hot = UI_Key(0)
|
||||
}
|
||||
}
|
||||
// profile_end()
|
||||
|
||||
signal.resizing = cast(b8) is_active && (within_resize_range || ui.active_start_signal.resizing)
|
||||
ui.hot_resizable = cast(b32) (is_hot && within_resize_range) || signal.resizing
|
||||
|
||||
// State Deltas update
|
||||
// profile_begin( "state deltas upate")
|
||||
if is_hot
|
||||
{
|
||||
box.hot_delta += frame_delta
|
||||
@ -153,11 +159,14 @@ ui_signal_from_box :: proc ( box : ^ UI_Box ) -> UI_Signal
|
||||
else {
|
||||
box.disabled_delta = 0
|
||||
}
|
||||
// profile_end()
|
||||
|
||||
signal.dragging = cast(b8) is_active && ( ! within_resize_range && ! ui.active_start_signal.resizing)
|
||||
|
||||
// Update style if not in default state
|
||||
{
|
||||
// profile("Update style")
|
||||
|
||||
if is_hot
|
||||
{
|
||||
if ! was_hot {
|
||||
|
@ -7,6 +7,8 @@ UI_Widget :: struct {
|
||||
|
||||
ui_widget :: proc( label : string, flags : UI_BoxFlags ) -> (widget : UI_Widget)
|
||||
{
|
||||
// profile(#procedure)
|
||||
|
||||
widget.box = ui_box_make( flags, label )
|
||||
widget.signal = ui_signal_from_box( widget.box )
|
||||
return
|
||||
@ -14,6 +16,8 @@ ui_widget :: proc( label : string, flags : UI_BoxFlags ) -> (widget : UI_Widget)
|
||||
|
||||
ui_button :: proc( label : string, flags : UI_BoxFlags = {} ) -> (btn : UI_Widget)
|
||||
{
|
||||
// profile(#procedure)
|
||||
|
||||
btn_flags := UI_BoxFlags { .Mouse_Clickable, .Focusable, .Click_To_Focus }
|
||||
btn.box = ui_box_make( btn_flags | flags, label )
|
||||
btn.signal = ui_signal_from_box( btn.box )
|
||||
@ -22,20 +26,22 @@ ui_button :: proc( label : string, flags : UI_BoxFlags = {} ) -> (btn : UI_Widge
|
||||
|
||||
ui_text :: proc( label : string, content : StringCached, flags : UI_BoxFlags = {} ) -> UI_Widget
|
||||
{
|
||||
// profile(#procedure)
|
||||
|
||||
state := get_state(); using state
|
||||
|
||||
box := ui_box_make( flags, label )
|
||||
signal := ui_signal_from_box( box )
|
||||
|
||||
text_size := measure_text_size( content.str, box.style.font, box.style.font_size, 0 )
|
||||
|
||||
box.text = content
|
||||
box.style.layout.size = text_size
|
||||
box.text = content
|
||||
box.style.layout.size_to_text = true
|
||||
return { box, signal }
|
||||
}
|
||||
|
||||
ui_space :: proc( label : string, flags : UI_BoxFlags = {} ) -> UI_Widget
|
||||
{
|
||||
// profile(#procedure)
|
||||
|
||||
space_str := str_intern( " " )
|
||||
|
||||
state := get_state(); using state
|
||||
@ -43,14 +49,15 @@ ui_space :: proc( label : string, flags : UI_BoxFlags = {} ) -> UI_Widget
|
||||
box := ui_box_make( flags, label )
|
||||
signal := ui_signal_from_box( box )
|
||||
|
||||
text_size := measure_text_size( space_str.str, box.style.font, box.style.font_size, 0 )
|
||||
box.text = space_str
|
||||
box.style.layout.size = text_size
|
||||
box.text = space_str
|
||||
box.style.layout.size_to_text = true
|
||||
return { box, signal }
|
||||
}
|
||||
|
||||
ui_tab :: proc( label : string, flags : UI_BoxFlags = {} ) -> UI_Widget
|
||||
{
|
||||
// profile(#procedure)
|
||||
|
||||
tab_str := str_intern( "\t" )
|
||||
|
||||
state := get_state(); using state
|
||||
@ -58,8 +65,8 @@ ui_tab :: proc( label : string, flags : UI_BoxFlags = {} ) -> UI_Widget
|
||||
box := ui_box_make( flags, label )
|
||||
signal := ui_signal_from_box( box )
|
||||
|
||||
text_size := measure_text_size( tab_str.str, box.style.font, box.style.font_size, 0 )
|
||||
box.text = tab_str
|
||||
box.style.layout.size = text_size
|
||||
box.text = tab_str
|
||||
|
||||
box.style.layout.size_to_text = true
|
||||
return { box, signal }
|
||||
}
|
||||
|
@ -158,6 +158,7 @@ push-location $path_root
|
||||
$build_args += $flag_thread_count + $CoreCount_Physical
|
||||
$build_args += $flag_optimize_none
|
||||
# $build_args += $flag_optimize_minimal
|
||||
# $build_args += $flag_optimize_speed
|
||||
$build_args += $flag_debug
|
||||
$build_args += $flag_pdb_name + $pdb
|
||||
$build_args += $flag_subsystem + 'windows'
|
||||
@ -225,7 +226,9 @@ push-location $path_root
|
||||
# $build_args += ($flag_collection + $pkg_collection_thirdparty)
|
||||
$build_args += $flag_use_separate_modules
|
||||
$build_args += $flag_thread_count + $CoreCount_Physical
|
||||
$build_args += $flag_optimize_none
|
||||
# $build_args += $flag_optimize_none
|
||||
# $build_args += $flag_optimize_minimal
|
||||
$build_args += $flag_optimize_speed
|
||||
$build_args += $flag_debug
|
||||
$build_args += $flag_pdb_name + $pdb
|
||||
$build_args += $flag_subsystem + 'windows'
|
||||
|
Loading…
Reference in New Issue
Block a user