diff --git a/.vscode/launch.json b/.vscode/launch.json index 4a6a8b8..eeee589 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,9 +8,9 @@ "type": "lldb", "request": "launch", "name": "Debug", - "program": "${workspaceFolder}/", + "program": "${workspaceFolder}/build/sectr_host.exe", "args": [], - "cwd": "${workspaceFolder}" + "cwd": "${workspaceFolder}/build" } ] } \ No newline at end of file diff --git a/Debug.rdbg b/Debug.rdbg index 1306f1a..91175ec 100644 Binary files a/Debug.rdbg and b/Debug.rdbg differ diff --git a/code/alias_raylib.odin b/code/alias_raylib.odin index 22e2f69..680a2e1 100644 --- a/code/alias_raylib.odin +++ b/code/alias_raylib.odin @@ -2,3 +2,4 @@ package sectr import rl "vendor:raylib" +Font :: rl.Font diff --git a/code/api.odin b/code/api.odin new file mode 100644 index 0000000..adbed6c --- /dev/null +++ b/code/api.odin @@ -0,0 +1,120 @@ +package sectr + +import "core:dynlib" +import "core:fmt" +import "core:mem" +import "core:os" +import "core:strings" +import rl "vendor:raylib" + +Path_Assets :: "../assets/" + +ModuleAPI :: struct { + lib : dynlib.Library, + load_time : os.File_Time, + lib_version : i32, + + startup : type_of( startup ), + shutdown : type_of( sectr_shutdown), + reload : type_of( reload ), + update : type_of( update ), + render : type_of( render ) +} + +Memory :: struct { + persistent : ^ mem.Arena, + transient : ^ mem.Arena +} + +memory : Memory + +@export +startup :: proc( persistent, transient : ^ mem.Arena ) +{ + memory.persistent = persistent + memory.transient = transient + context.allocator = mem.arena_allocator( transient ) + context.temp_allocator = mem.arena_allocator( transient ) + + state := cast(^State) memory.persistent + + // Rough setup of window with rl stuff + screen_width : i32 = 1280 + screen_height : i32 = 1000 + win_title : cstring = "Sectr Prototype" + rl.InitWindow( screen_width, screen_height, win_title ) + + // Determining current monitor and setting the target frametime based on it.. + monitor_id := rl.GetCurrentMonitor() + monitor_refresh_hz := rl.GetMonitorRefreshRate( monitor_id ) + rl.SetTargetFPS( monitor_refresh_hz ) + fmt.println( "Set target FPS to: %v", monitor_refresh_hz ) + + // Basic Font Setup + { + path_rec_mono_semicasual_reg := strings.concatenate( { Path_Assets, "RecMonoSemicasual-Regular-1.084.ttf" }) + cstr := strings.clone_to_cstring(path_rec_mono_semicasual_reg) + font_rec_mono_semicasual_reg = rl.LoadFontEx( cstr, 24, nil, 0 ) + delete( cstr) + + rl.GuiSetFont( font_rec_mono_semicasual_reg ) // TODO(Ed) : Does this do anything? + default_font = font_rec_mono_semicasual_reg + } +} + +// For some reason odin's symbols conflict with native foreign symbols... +@export +sectr_shutdown :: proc() +{ + rl.UnloadFont( font_rec_mono_semicasual_reg ) + rl.CloseWindow() +} + +@export +reload :: proc( persistent, transient : ^ mem.Arena ) +{ + memory.persistent = persistent + memory.transient = transient + context.allocator = mem.arena_allocator( persistent ) + context.temp_allocator = mem.arena_allocator( transient ) +} + +@export +update :: proc() -> b32 +{ + should_shutdown : b32 = ! cast(b32) rl.WindowShouldClose() + return should_shutdown +} + +draw_text_y : f32 = 50 +@export +render :: proc() +{ + rl.BeginDrawing() + rl.ClearBackground( Color_BG ) + defer { + rl.DrawFPS( 0, 0 ) + rl.EndDrawing() + // Note(Ed) : Polls input as well. + } + + draw_text :: proc( format : string, args : ..any ) + { + @static + draw_text_scratch : [Kilobyte * 64]u8 + if ( draw_text_y > 500 ) { + draw_text_y = 50 + } + + content := fmt.bprintf( draw_text_scratch[:], format, ..args ) + debug_text( content, 25, draw_text_y ) + + draw_text_y += 16 + } + + draw_text( "Monitor : %v", rl.GetMonitorName(0) ) + draw_text( "Screen Width : %v", rl.GetScreenWidth() ) + draw_text( "Screen Height: %v", rl.GetScreenHeight() ) + + draw_text_y = 50 +} diff --git a/code/colors.odin b/code/colors.odin index d13bec6..0fc981c 100644 --- a/code/colors.odin +++ b/code/colors.odin @@ -2,7 +2,9 @@ package sectr import rl "vendor:raylib" -Color_BG :: rl.Color { 41, 41, 45, 255 } -Color_BG_TextBox :: rl.Color { 32, 32, 32, 255 } -Color_Frame_Hover :: rl.Color { 122, 122, 125, 255 } -Color_Frame_Select :: rl.Color { 188, 188, 188, 255 } +Color :: rl.Color + +Color_BG :: Color { 41, 41, 45, 255 } +Color_BG_TextBox :: Color { 32, 32, 32, 255 } +Color_Frame_Hover :: Color { 122, 122, 125, 255 } +Color_Frame_Select :: Color { 188, 188, 188, 255 } diff --git a/code/engine_loop.odin b/code/engine_loop.odin deleted file mode 100644 index a07a29c..0000000 --- a/code/engine_loop.odin +++ /dev/null @@ -1,51 +0,0 @@ -package sectr - -import "core:fmt" - -import rl "vendor:raylib" - -draw_text_y : f32 = 50 - -run_cycle :: proc( running : ^b32 ) -{ - for ; running^ ; - { - if rl.WindowShouldClose() { - running^ = false; - } - - // Logic Update - { - - } - - // Rendering - { - rl.BeginDrawing() - rl.ClearBackground( Color_BG ) - defer { - rl.DrawFPS( 0, 0 ) - rl.EndDrawing() - // Note(Ed) : Polls input as well. - } - - draw_text :: proc( format : string, args : ..any ) - { - @static draw_text_scratch : [65536]u8 - if ( draw_text_y > 500 ) { - draw_text_y = 50 - } - content := fmt.bprintf( draw_text_scratch[:], format, ..args ) - debug_text( content, 25, draw_text_y ) - draw_text_y += 16 - } - - draw_text( "Monitor : %v", rl.GetMonitorName(0) ) - draw_text( "Screen Width : %v", rl.GetScreenWidth() ) - draw_text( "Screen Height: %v", rl.GetScreenHeight() ) - - - draw_text_y = 50 - } - } -} diff --git a/code/env.odin b/code/env.odin new file mode 100644 index 0000000..3c60c8e --- /dev/null +++ b/code/env.odin @@ -0,0 +1,25 @@ +package sectr + +State :: struct { + project : Project, + + screen_width : i32, + screen_height : i32, + + monitor_id : i32, + monitor_refresh_hz : i32, + + engine_refresh_hz : i32, + engine_refresh_target : i32, + + draw_debug_text_y : f32 +} + +Project :: struct { + // TODO(Ed) : Support multiple workspaces + workspace : Workspace +} + +Workspace :: struct { + +} diff --git a/code/filesystem.odin b/code/filesystem.odin new file mode 100644 index 0000000..6fa8801 --- /dev/null +++ b/code/filesystem.odin @@ -0,0 +1,33 @@ +package sectr + +import "core:fmt" +import "core:os" +import "core:runtime" + +copy_file_sync :: proc( path_src, path_dst: string ) -> bool +{ + file_size : i64 + { + path_info, result := os.stat( path_src, context.temp_allocator ) + if result != os.ERROR_NONE { + fmt.println("Error getting file info: ", result ) + return false + } + file_size = path_info.size + } + + src_content, result := os.read_entire_file( path_src, context.temp_allocator ) + if ! result { + fmt.println( "Failed to read file to copy" ) + runtime.debug_trap() + return false + } + + result = os.write_entire_file( path_dst, src_content, false ) + if ! result { + fmt.println( "Failed to copy file") + runtime.debug_trap() + return false + } + return true +} diff --git a/code/host/host.odin b/code/host/host.odin new file mode 100644 index 0000000..401f4ef --- /dev/null +++ b/code/host/host.odin @@ -0,0 +1,177 @@ +package host + +import "core:dynlib" +import "core:io" +import "core:fmt" +import "core:log" +import "core:mem" +import "core:mem/virtual" + Byte :: 1 + Kilobyte :: 1024 * Byte + Megabyte :: 1024 * Kilobyte + Gigabyte :: 1024 * Megabyte + Terabyte :: 1024 * Gigabyte + Petabyte :: 1024 * Terabyte + Exabyte :: 1024 * Petabyte +import "core:os" +import "core:runtime" +import "core:strings" +import rl "vendor:raylib" +import sectr "../." + +RuntimeState :: struct { + running : b32, + + + memory : VMemChunk, + + sectr_api : sectr.ModuleAPI, +} + +VMemChunk :: struct { + sarena : virtual.Arena, + eng_persistent : mem.Arena, + eng_transient : mem.Arena, + env_persistent : mem.Arena, + env_transient : mem.Arena +} + +setup_engine_memory :: proc () -> VMemChunk +{ + memory : VMemChunk + using memory + + arena_init :: mem.arena_init + ptr_offset :: mem.ptr_offset + slice_ptr :: mem.slice_ptr + + // Setup the static arena for the entire application + if result := virtual.arena_init_static( & sarena, Gigabyte * 2, Gigabyte * 2 ); + result != runtime.Allocator_Error.None + { + // TODO(Ed) : Setup a proper logging interface + fmt. printf( "Failed to allocate memory for the engine" ) + runtime.debug_trap() + os. exit( -1 ) + // TODO(Ed) : Figure out the error code enums.. + } + + // For now I'm making persistent sections each 128 meg and transient sections w/e is left over / 2 (one for engine the other for the env) + persistent_size := Megabyte * 128 + transient_size := (Gigabyte * 2 - persistent_size * 2) / 2 + + block := memory.sarena.curr_block + + // Try to get a slice for each segment + eng_persistent_slice := slice_ptr( block.base, persistent_size) + eng_transient_slice := slice_ptr( & eng_persistent_slice[persistent_size - 1], transient_size) + env_persistent_slice := slice_ptr( & eng_transient_slice [transient_size - 1], persistent_size) + env_transient_slice := slice_ptr( & env_persistent_slice[persistent_size -1], transient_size) + + arena_init( & eng_persistent, eng_persistent_slice ) + arena_init( & eng_transient, eng_transient_slice ) + arena_init( & env_persistent, env_persistent_slice ) + arena_init( & env_transient, env_transient_slice ) + + return memory; +} + +load_sectr_api :: proc ( version_id : i32 ) -> sectr.ModuleAPI +{ + loaded_module : sectr.ModuleAPI + + load_time, + result := os.last_write_time_by_name("sectr.dll") + if result != os.ERROR_NONE { + fmt. println("Could not resolve the last write time for sectr.dll") + runtime.debug_trap() + return {} + } + + lock_file := fmt.tprintf( "sectr_{0}_locked.dll", version_id ) + sectr.copy_file_sync( "sectr.dll", lock_file ) + + lib, load_result := dynlib.load_library( lock_file ) + if ! load_result { + fmt. println( "Failed to load the sectr module." ) + runtime.debug_trap() + return {} + } + + loaded_module = { + lib = lib, + load_time = load_time, + lib_version = version_id, + + startup = cast( type_of( sectr.startup )) dynlib.symbol_address( lib, "startup" ), + shutdown = cast( type_of( sectr.sectr_shutdown )) dynlib.symbol_address( lib, "sectr_shutdown" ), + reload = cast( type_of( sectr.reload )) dynlib.symbol_address( lib, "reload" ), + update = cast( type_of( sectr.update )) dynlib.symbol_address( lib, "update" ), + render = cast( type_of( sectr.render )) dynlib.symbol_address( lib, "render" ) + } + return loaded_module +} + +main :: proc() +{ + fmt.println("Hellope!") + + // Basic Giant VMem Block + memory : VMemChunk + { + // By default odin uses a growing arena for the runtime context + // We're going to make it static for the prototype and separate it from the 'project' memory. + // Then shove the context allocator for the engine to it. + // The project's context will use its own subsection arena allocator. + memory = setup_engine_memory() + context.allocator = mem.arena_allocator( & memory.eng_persistent ) + context.temp_allocator = mem.arena_allocator( & memory.eng_transient ) + } + + // Load the Enviornment API for the first-time + sectr_api : sectr.ModuleAPI + { + sectr_api = load_sectr_api( 1 ) + if sectr_api.lib_version == 0 { + fmt. println( "Failed to initially load the sectr module" ) + runtime.debug_trap() + os. exit( -1 ) + } + } + + state : RuntimeState + state.running = true; + // state.monitor_id = monitor_id + // state.screen_width = screen_width + // state.screen_height = screen_height + // state.monitor_id = monitor_id + // state.monitor_refresh_hz = monitor_refresh_hz + state.memory = memory + state.sectr_api = sectr_api + + state.sectr_api.startup( & memory.env_persistent, & memory.env_persistent ) + + // TODO(Ed) : This should return a end status so that we know the reason the engine stopped. + for ; state.running ; + { + // Hot-Reload + // TODO(ED) : Detect if currently loaded code is outdated. + { + // state.sectr_api.reload() + } + + // Logic Update + state.running = state.sectr_api.update() + + // Rendering + state.sectr_api.render() + } + + // Determine how the run_cyle completed, if it failed due to an error, + // fallback the env to a failsafe state and reload the run_cycle. + { + // TODO(Ed): Implement this. + } + + state.sectr_api.shutdown() +} diff --git a/code/launch.odin b/code/launch.odin deleted file mode 100644 index 7999de9..0000000 --- a/code/launch.odin +++ /dev/null @@ -1,42 +0,0 @@ -package sectr - -import "core:strings" - -import rl "vendor:raylib" - -Path_Assets :: "../assets/" - - -WindowState :: struct { - -} - -main :: proc() -{ - // Rough setup of window with rl stuff - screen_width : i32 = 1280 - screen_height : i32 = 1000 - win_title : cstring = "Sectr Prototype" - rl.InitWindow( screen_width, screen_height, win_title ) - defer { - rl.CloseWindow() - } - - monitor_id := rl.GetCurrentMonitor() - monitor_refresh_rate := rl.GetMonitorRefreshRate( monitor_id ) - rl.SetTargetFPS( monitor_refresh_rate ) - - // Basic Font Setup - { - path_rec_mono_semicasual_reg := strings.concatenate( { Path_Assets, "RecMonoSemicasual-Regular-1.084.ttf" } ) - cstr := strings.clone_to_cstring(path_rec_mono_semicasual_reg) - font_rec_mono_semicasual_reg = rl.LoadFontEx( cstr, 24, nil, 0 ) - delete( cstr ) - - rl.GuiSetFont( font_rec_mono_semicasual_reg ) // TODO(Ed) : Does this do anything? - default_font = font_rec_mono_semicasual_reg - } - - running : b32 = true - run_cycle( & running ) -} diff --git a/code/memory.odin b/code/memory.odin index 850d736..8b66b50 100644 --- a/code/memory.odin +++ b/code/memory.odin @@ -1,8 +1,21 @@ package sectr -kilobytes :: proc ( kb : $integer_type ) -> integer_type { +import "core:fmt" +import "core:mem" +import "core:mem/virtual" +import "core:runtime" + +Byte :: 1 +Kilobyte :: 1024 * Byte +Megabyte :: 1024 * Kilobyte +Gigabyte :: 1024 * Megabyte +Terabyte :: 1024 * Gigabyte +Petabyte :: 1024 * Terabyte +Exabyte :: 1024 * Petabyte + +kilobytes :: proc ( kb : $ integer_type ) -> integer_type { return kb * 1024 } -megabytes :: proc ( kb : $integer_type ) -> integer_type { +megabytes :: proc ( kb : $ integer_type ) -> integer_type { return kb * 1024 * 1024 } diff --git a/code/text.odin b/code/text.odin index b4fc5a9..c265678 100644 --- a/code/text.odin +++ b/code/text.odin @@ -4,15 +4,15 @@ import "core:unicode/utf8" import rl "vendor:raylib" -font_rec_mono_semicasual_reg : rl.Font; -default_font : rl.Font +font_rec_mono_semicasual_reg : Font; +default_font : Font debug_text :: proc( content : string, x, y : f32, size : f32 = 16.0, color : rl.Color = rl.WHITE, font : rl.Font = default_font ) { if len( content ) == 0 { return } - runes := utf8.string_to_runes( content ) + runes := utf8.string_to_runes( content, context.temp_allocator ) rl.DrawTextCodepoints( font, raw_data(runes), cast(i32) len(runes), diff --git a/scripts/build.ps1 b/scripts/build.ps1 index 7131996..47feb32 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -24,6 +24,7 @@ $flag_show_more_timings = '-show-more-timings' $flag_thread_count = '-thread-count:' $flag_collection = '-collection:' $flag_build_mode = '-build-mode:' +$flag_build_mode_dll = '-build-mode:dll' $flag_no_bounds_check = '-no-bounds-check' $flag_disable_assert = '-disable-assert' $flag_no_thread_local = '-no-thread-local' @@ -58,12 +59,12 @@ push-location $path_root push-location $path_code $project_name = 'sectr' - $executable = join-path $path_build ($project_name + '.exe') - $pdb = join-path $path_build ($project_name + '.pdb') + $executable = join-path $path_build ($project_name + '_host.exe') + $pdb = join-path $path_build ($project_name + '_host.pdb') $build_args = @() $build_args += $flag_build - $build_args += '.' + $build_args += './host' $build_args += $flag_output_path + $executable $build_args += $flag_optimize_none $build_args += $flag_debug @@ -72,6 +73,20 @@ push-location $path_root & odin $build_args + $module_dll = join-path $path_build ( $project_name + '.dll' ) + $pdb = join-path $path_build ( $project_name + '.pdb' ) + + $build_args = @() + $build_args += $flag_build + $build_args += '.' + $build_args += $flag_build_mode_dll + $build_args += $flag_output_path + $module_dll + $build_args += $flag_optimize_none + $build_args += $flag_debug + $build_args += $flag_pdb_name + $pdb + + & odin $build_args + Pop-Location } build-prototype