diff --git a/main.odin b/main.odin deleted file mode 100644 index ca275b9..0000000 --- a/main.odin +++ /dev/null @@ -1,252 +0,0 @@ -package main - -import rl "libraries/raylib" -import "core:log" - -import "libraries/back" -import "core:mem" -import "core:c" -import "base:intrinsics" -import "base:runtime" -import "core:strings" -import "core:fmt" - -when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 { - @(private="file") web_context: runtime.Context - - @export main_start :: proc "c" () { - context = runtime.default_context() - - // The WASM allocator doesn't seem to work properly in combination with - // emscripten. There is some kind of conflict with how the manage memory. - // So this sets up an allocator that uses emscripten's malloc. - context.allocator = emscripten_allocator() - runtime.init_global_temporary_allocator(1*mem.Megabyte) - - // Since we now use js_wasm32 we should be able to remove this and use - // context.logger = log.create_console_logger(). However, that one produces - // extra newlines on web. So it's a bug in that core lib. - context.logger = create_emscripten_logger() - - web_context = context - - init() - } - - @export main_update :: proc "c" () -> bool { - context = web_context - update() - return should_run() - } - - @export main_end :: proc "c" () { - context = web_context - shutdown() - } - - @export web_window_size_changed :: proc "c" (w: c.int, h: c.int) { - context = web_context - parent_window_size_changed(int(w), int(h)) - } - - /////////////////////////////////////////////////////// - /// EMSCRIPTEN ALLOCATOR /// - /////////////////////////////////////////////////////// - - // This will create bindings to emscripten's implementation of libc - // memory allocation features. - @(default_calling_convention = "c") - foreign { - calloc :: proc(num, size: c.size_t) -> rawptr --- - free :: proc(ptr: rawptr) --- - malloc :: proc(size: c.size_t) -> rawptr --- - realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr --- - } - - emscripten_allocator :: proc "contextless" () -> mem.Allocator { - return mem.Allocator{emscripten_allocator_proc, nil} - } - - emscripten_allocator_proc :: proc(allocator_data: rawptr,mode: mem.Allocator_Mode,size, alignment: int,old_memory: rawptr,old_size: int,location := #caller_location) -> (data: []byte, err: mem.Allocator_Error) { - // These aligned alloc procs are almost indentical those in - // `_heap_allocator_proc` in `core:os`. Without the proper alignment you - // cannot use maps and simd features. - - aligned_alloc :: proc(size, alignment: int, zero_memory: bool, old_ptr: rawptr = nil) -> ([]byte, mem.Allocator_Error) { - a := max(alignment, align_of(rawptr)) - space := size + a - 1 - - allocated_mem: rawptr - if old_ptr != nil { - original_old_ptr := mem.ptr_offset((^rawptr)(old_ptr), -1)^ - allocated_mem = realloc(original_old_ptr, c.size_t(space+size_of(rawptr))) - } else if zero_memory { - // calloc automatically zeros memory, but it takes a number + size - // instead of just size. - allocated_mem = calloc(c.size_t(space+size_of(rawptr)), 1) - } else { - allocated_mem = malloc(c.size_t(space+size_of(rawptr))) - } - aligned_mem := rawptr(mem.ptr_offset((^u8)(allocated_mem), size_of(rawptr))) - - ptr := uintptr(aligned_mem) - aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a) - diff := int(aligned_ptr - ptr) - if (size + diff) > space || allocated_mem == nil { - return nil, .Out_Of_Memory - } - - aligned_mem = rawptr(aligned_ptr) - mem.ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem - - return mem.byte_slice(aligned_mem, size), nil - } - - aligned_free :: proc(p: rawptr) { - if p != nil { - free(mem.ptr_offset((^rawptr)(p), -1)^) - } - } - - aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> ([]byte, mem.Allocator_Error) { - if p == nil { - return nil, nil - } - return aligned_alloc(new_size, new_alignment, true, p) - } - - switch mode { - case .Alloc: - return aligned_alloc(size, alignment, true) - - case .Alloc_Non_Zeroed: - return aligned_alloc(size, alignment, false) - - case .Free: - aligned_free(old_memory) - return nil, nil - - case .Resize: - if old_memory == nil { - return aligned_alloc(size, alignment, true) - } - - bytes := aligned_resize(old_memory, old_size, size, alignment) or_return - - // realloc doesn't zero the new bytes, so we do it manually. - if size > old_size { - new_region := raw_data(bytes[old_size:]) - intrinsics.mem_zero(new_region, size - old_size) - } - - return bytes, nil - - case .Resize_Non_Zeroed: - if old_memory == nil { - return aligned_alloc(size, alignment, false) - } - - return aligned_resize(old_memory, old_size, size, alignment) - - case .Query_Features: - set := (^mem.Allocator_Mode_Set)(old_memory) - if set != nil { - set^ = {.Alloc, .Free, .Resize, .Query_Features} - } - return nil, nil - - case .Free_All, .Query_Info: - return nil, .Mode_Not_Implemented - } - return nil, .Mode_Not_Implemented - } - - /////////////////////////////////////////////////////// - /// EMSCRIPTEN LOGGER /// - /////////////////////////////////////////////////////// - Emscripten_Logger_Opts :: log.Options{.Level, .Short_File_Path, .Line} - - create_emscripten_logger :: proc (lowest := log.Level.Debug, opt := Emscripten_Logger_Opts) -> log.Logger { - return log.Logger{data = nil, procedure = logger_proc, lowest_level = lowest, options = opt} - } - - // This create's a binding to `puts` which will be linked in as part of the - // emscripten runtime. - @(default_calling_convention = "c") - foreign { - puts :: proc(buffer: cstring) -> c.int --- - } - - @(private="file") - logger_proc :: proc(logger_data: rawptr,level: log.Level,text: string,options: log.Options,location := #caller_location) { - b := strings.builder_make(context.temp_allocator) - strings.write_string(&b, Level_Headers[level]) - do_location_header(options, &b, location) - fmt.sbprint(&b, text) - - if bc, bc_err := strings.to_cstring(&b); bc_err == nil { - puts(bc) - } - } - - @(private="file") - Level_Headers := [?]string { - 0 ..< 10 = "[DEBUG] --- ", - 10 ..< 20 = "[INFO ] --- ", - 20 ..< 30 = "[WARN ] --- ", - 30 ..< 40 = "[ERROR] --- ", - 40 ..< 50 = "[FATAL] --- ", - } - - @(private="file") - do_location_header :: proc(opts: log.Options, buf: ^strings.Builder, location := #caller_location) { - if log.Location_Header_Opts & opts == nil { - return - } - fmt.sbprint(buf, "[") - file := location.file_path - if .Short_File_Path in opts { - last := 0 - for r, i in location.file_path { - if r == '/' { - last = i + 1 - } - } - file = location.file_path[last:] - } - - if log.Location_File_Opts & opts != nil { - fmt.sbprint(buf, file) - } - if .Line in opts { - if log.Location_File_Opts & opts != nil { - fmt.sbprint(buf, ":") - } - fmt.sbprint(buf, location.line) - } - - if .Procedure in opts { - if (log.Location_File_Opts | {.Line}) & opts != nil { - fmt.sbprint(buf, ":") - } - fmt.sbprintf(buf, "%s()", location.procedure) - } - - fmt.sbprint(buf, "] ") - } -} else { - main :: proc() { - // exe_path := os.args[0] - // exe_dir := filepath.dir(string(exe_path)) - // os.set_working_directory(exe_dir) - rl.ChangeDirectory(rl.GetApplicationDirectory()) - back.register_segfault_handler() - context.logger = log.create_console_logger() - - init() - for should_run() { - update() - } - shutdown() - } -} diff --git a/main_desktop.odin b/main_desktop.odin new file mode 100644 index 0000000..5aea694 --- /dev/null +++ b/main_desktop.odin @@ -0,0 +1,20 @@ +package main + +import rl "libraries/raylib" +import "libraries/back" +import "core:log" + +main :: proc() { + // exe_path := os.args[0] + // exe_dir := filepath.dir(string(exe_path)) + // os.set_working_directory(exe_dir) + rl.ChangeDirectory(rl.GetApplicationDirectory()) + back.register_segfault_handler() + context.logger = log.create_console_logger() + + init() + for should_run() { + update() + } + shutdown() +} diff --git a/main_web.odin b/main_web.odin new file mode 100644 index 0000000..b6b34ea --- /dev/null +++ b/main_web.odin @@ -0,0 +1,231 @@ +#+build wasm32, wasm64p32 + +package main + +import "core:log" +import "core:mem" +import "core:c" +import "base:intrinsics" +import "base:runtime" +import "core:strings" +import "core:fmt" + +@(private="file") web_context: runtime.Context + +@export main_start :: proc "c" () { + context = runtime.default_context() + + // The WASM allocator doesn't seem to work properly in combination with + // emscripten. There is some kind of conflict with how the manage memory. + // So this sets up an allocator that uses emscripten's malloc. + context.allocator = emscripten_allocator() + runtime.init_global_temporary_allocator(1*mem.Megabyte) + + // Since we now use js_wasm32 we should be able to remove this and use + // context.logger = log.create_console_logger(). However, that one produces + // extra newlines on web. So it's a bug in that core lib. + context.logger = create_emscripten_logger() + + web_context = context + + init() +} + +@export main_update :: proc "c" () -> bool { + context = web_context + update() + return should_run() +} + +@export main_end :: proc "c" () { + context = web_context + shutdown() +} + +@export web_window_size_changed :: proc "c" (w: c.int, h: c.int) { + context = web_context + parent_window_size_changed(int(w), int(h)) +} + +/////////////////////////////////////////////////////// +/// EMSCRIPTEN ALLOCATOR /// +/////////////////////////////////////////////////////// + +// This will create bindings to emscripten's implementation of libc +// memory allocation features. +@(default_calling_convention = "c") +foreign { + calloc :: proc(num, size: c.size_t) -> rawptr --- + free :: proc(ptr: rawptr) --- + malloc :: proc(size: c.size_t) -> rawptr --- + realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr --- +} + +emscripten_allocator :: proc "contextless" () -> mem.Allocator { + return mem.Allocator{emscripten_allocator_proc, nil} +} + +emscripten_allocator_proc :: proc(allocator_data: rawptr,mode: mem.Allocator_Mode,size, alignment: int,old_memory: rawptr,old_size: int,location := #caller_location) -> (data: []byte, err: mem.Allocator_Error) { + // These aligned alloc procs are almost indentical those in + // `_heap_allocator_proc` in `core:os`. Without the proper alignment you + // cannot use maps and simd features. + + aligned_alloc :: proc(size, alignment: int, zero_memory: bool, old_ptr: rawptr = nil) -> ([]byte, mem.Allocator_Error) { + a := max(alignment, align_of(rawptr)) + space := size + a - 1 + + allocated_mem: rawptr + if old_ptr != nil { + original_old_ptr := mem.ptr_offset((^rawptr)(old_ptr), -1)^ + allocated_mem = realloc(original_old_ptr, c.size_t(space+size_of(rawptr))) + } else if zero_memory { + // calloc automatically zeros memory, but it takes a number + size + // instead of just size. + allocated_mem = calloc(c.size_t(space+size_of(rawptr)), 1) + } else { + allocated_mem = malloc(c.size_t(space+size_of(rawptr))) + } + aligned_mem := rawptr(mem.ptr_offset((^u8)(allocated_mem), size_of(rawptr))) + + ptr := uintptr(aligned_mem) + aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a) + diff := int(aligned_ptr - ptr) + if (size + diff) > space || allocated_mem == nil { + return nil, .Out_Of_Memory + } + + aligned_mem = rawptr(aligned_ptr) + mem.ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem + + return mem.byte_slice(aligned_mem, size), nil + } + + aligned_free :: proc(p: rawptr) { + if p != nil { + free(mem.ptr_offset((^rawptr)(p), -1)^) + } + } + + aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> ([]byte, mem.Allocator_Error) { + if p == nil { + return nil, nil + } + return aligned_alloc(new_size, new_alignment, true, p) + } + + switch mode { + case .Alloc: + return aligned_alloc(size, alignment, true) + + case .Alloc_Non_Zeroed: + return aligned_alloc(size, alignment, false) + + case .Free: + aligned_free(old_memory) + return nil, nil + + case .Resize: + if old_memory == nil { + return aligned_alloc(size, alignment, true) + } + + bytes := aligned_resize(old_memory, old_size, size, alignment) or_return + + // realloc doesn't zero the new bytes, so we do it manually. + if size > old_size { + new_region := raw_data(bytes[old_size:]) + intrinsics.mem_zero(new_region, size - old_size) + } + + return bytes, nil + + case .Resize_Non_Zeroed: + if old_memory == nil { + return aligned_alloc(size, alignment, false) + } + + return aligned_resize(old_memory, old_size, size, alignment) + + case .Query_Features: + set := (^mem.Allocator_Mode_Set)(old_memory) + if set != nil { + set^ = {.Alloc, .Free, .Resize, .Query_Features} + } + return nil, nil + + case .Free_All, .Query_Info: + return nil, .Mode_Not_Implemented + } + return nil, .Mode_Not_Implemented +} + +/////////////////////////////////////////////////////// +/// EMSCRIPTEN LOGGER /// +/////////////////////////////////////////////////////// +Emscripten_Logger_Opts :: log.Options{.Level, .Short_File_Path, .Line} + +create_emscripten_logger :: proc (lowest := log.Level.Debug, opt := Emscripten_Logger_Opts) -> log.Logger { + return log.Logger{data = nil, procedure = logger_proc, lowest_level = lowest, options = opt} +} + +// This create's a binding to `puts` which will be linked in as part of the +// emscripten runtime. +@(default_calling_convention = "c") +foreign { + puts :: proc(buffer: cstring) -> c.int --- +} + +@(private="file") logger_proc :: proc(logger_data: rawptr,level: log.Level,text: string,options: log.Options,location := #caller_location) { + b := strings.builder_make(context.temp_allocator) + strings.write_string(&b, Level_Headers[level]) + do_location_header(options, &b, location) + fmt.sbprint(&b, text) + + if bc, bc_err := strings.to_cstring(&b); bc_err == nil { + puts(bc) + } +} + +@(private="file") Level_Headers := [?]string { + 0 ..< 10 = "[DEBUG] --- ", + 10 ..< 20 = "[INFO ] --- ", + 20 ..< 30 = "[WARN ] --- ", + 30 ..< 40 = "[ERROR] --- ", + 40 ..< 50 = "[FATAL] --- ", +} + +@(private="file") do_location_header :: proc(opts: log.Options, buf: ^strings.Builder, location := #caller_location) { + if log.Location_Header_Opts & opts == nil { + return + } + fmt.sbprint(buf, "[") + file := location.file_path + if .Short_File_Path in opts { + last := 0 + for r, i in location.file_path { + if r == '/' { + last = i + 1 + } + } + file = location.file_path[last:] + } + + if log.Location_File_Opts & opts != nil { + fmt.sbprint(buf, file) + } + if .Line in opts { + if log.Location_File_Opts & opts != nil { + fmt.sbprint(buf, ":") + } + fmt.sbprint(buf, location.line) + } + + if .Procedure in opts { + if (log.Location_File_Opts | {.Line}) & opts != nil { + fmt.sbprint(buf, ":") + } + fmt.sbprintf(buf, "%s()", location.procedure) + } + + fmt.sbprint(buf, "] ") +} diff --git a/signal_handler_linux.odin b/signal_handler_linux.odin new file mode 100644 index 0000000..d5cffdf --- /dev/null +++ b/signal_handler_linux.odin @@ -0,0 +1,36 @@ +#+build linux + +package main + +import "libraries/back" +import "core:sys/posix" +import "base:runtime" +import "core:mem" +import "core:fmt" + +_register_illegal_instruction_handler :: proc() { + posix.signal(.SIGILL, proc "c" (code: posix.Signal) { + context = runtime.default_context() + + space: [16*mem.Kilobyte]byte + arena: mem.Arena + mem.arena_init(&arena, space[:]) + allocator := mem.arena_allocator(&arena) + + context.allocator = allocator + context.temp_allocator = allocator + + backtrace: { + lines, err := back.lines(back.trace(), allocator, allocator) + if err != nil { + fmt.eprintf("Exception (Code: %i)\nCould not get backtrace: %v\n", code, err) + break backtrace + } + + fmt.eprintf("Exception (Code: %i)\n[back trace]\n", code) + back.print(lines, temp_allocator=allocator) + } + + runtime.exit(int(code)) + }) +} diff --git a/signal_handler_other.odin b/signal_handler_other.odin new file mode 100644 index 0000000..f429685 --- /dev/null +++ b/signal_handler_other.odin @@ -0,0 +1,7 @@ +#+build !linux + +package main + +_register_illegal_instruction_handler :: proc() { + +}