diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 455a451e7..e547959aa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,9 +32,9 @@ jobs: gmake -C vendor/miniaudio/src ./odin check examples/all -vet -strict-style -disallow-do -target:netbsd_amd64 ./odin check examples/all -vet -strict-style -disallow-do -target:netbsd_arm64 - ./odin test tests/core/normal.odin -file -all-packages -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true - ./odin test tests/core/speed.odin -file -all-packages -o:speed -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true - ./odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true + ./odin test tests/core/normal.odin -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true + ./odin test tests/core/speed.odin -file -all-packages -vet -strict-style -disallow-do -o:speed -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true + ./odin test tests/vendor -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true (cd tests/issues; ./run.sh) build_freebsd: name: FreeBSD Build, Check, and Test @@ -60,9 +60,9 @@ jobs: gmake -C vendor/cgltf/src gmake -C vendor/miniaudio/src ./odin check examples/all -vet -strict-style -disallow-do -target:freebsd_amd64 - ./odin test tests/core/normal.odin -file -all-packages -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true - ./odin test tests/core/speed.odin -file -all-packages -o:speed -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true - ./odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true + ./odin test tests/core/normal.odin -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true + ./odin test tests/core/speed.odin -file -all-packages -vet -strict-style -disallow-do -o:speed -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true + ./odin test tests/vendor -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true (cd tests/issues; ./run.sh) ci: strategy: @@ -116,13 +116,13 @@ jobs: - name: Odin check examples/all run: ./odin check examples/all -strict-style - name: Normal Core library tests - run: ./odin test tests/core/normal.odin -file -all-packages -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true + run: ./odin test tests/core/normal.odin -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true - name: Optimized Core library tests - run: ./odin test tests/core/speed.odin -o:speed -file -all-packages -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true + run: ./odin test tests/core/speed.odin -o:speed -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true - name: Vendor library tests - run: ./odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true + run: ./odin test tests/vendor -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true - name: Internals tests - run: ./odin test tests/internal -all-packages -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true + run: ./odin test tests/internal -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true - name: GitHub Issue tests run: | cd tests/issues @@ -176,33 +176,33 @@ jobs: shell: cmd run: | call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat - odin run examples/demo -debug + odin run examples/demo -debug -vet -strict-style -disallow-do - name: Odin check examples/all shell: cmd run: | call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat - odin check examples/all -strict-style + odin check examples/all -vet -strict-style -disallow-do - name: Core library tests shell: cmd run: | call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat - odin test tests/core/normal.odin -file -all-packages -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true + odin test tests/core/normal.odin -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true - name: Optimized core library tests shell: cmd run: | call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat - odin test tests/core/speed.odin -o:speed -file -all-packages -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true + odin test tests/core/speed.odin -o:speed -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true - name: Vendor library tests shell: cmd run: | call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat copy vendor\lua\5.4\windows\*.dll . - odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true + odin test tests/vendor -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true - name: Odin internals tests shell: cmd run: | call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat - odin test tests/internal -all-packages -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true + odin test tests/internal -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true - name: Odin documentation tests shell: cmd run: | @@ -257,16 +257,16 @@ jobs: run: sudo apt-get install -y qemu-user qemu-user-static gcc-12-riscv64-linux-gnu libc6-riscv64-cross - name: Odin run - run: ./odin run examples/demo -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static" + run: ./odin run examples/demo -vet -strict-style -disallow-do -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static" - name: Odin run -debug - run: ./odin run examples/demo -debug -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static" + run: ./odin run examples/demo -debug -vet -strict-style -disallow-do -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static" - name: Normal Core library tests - run: ./odin test tests/core/normal.odin -file -all-packages -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static" + run: ./odin test tests/core/normal.odin -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static" - name: Optimized Core library tests - run: ./odin test tests/core/speed.odin -o:speed -file -all-packages -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static" + run: ./odin test tests/core/speed.odin -o:speed -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static" - name: Internals tests - run: ./odin test tests/internal -all-packages -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static" + run: ./odin test tests/internal -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static" diff --git a/base/runtime/thread_management.odin b/base/runtime/thread_management.odin new file mode 100644 index 000000000..cabd4691c --- /dev/null +++ b/base/runtime/thread_management.odin @@ -0,0 +1,34 @@ +package runtime + +Thread_Local_Cleaner :: #type proc "odin" () + +@(private="file") +thread_local_cleaners: [8]Thread_Local_Cleaner + +// Add a procedure that will be run at the end of a thread for the purpose of +// deallocating state marked as `thread_local`. +// +// Intended to be called in an `init` procedure of a package with +// dynamically-allocated memory that is stored in `thread_local` variables. +add_thread_local_cleaner :: proc "contextless" (p: Thread_Local_Cleaner) { + for &v in thread_local_cleaners { + if v == nil { + v = p + return + } + } + panic_contextless("There are no more thread-local cleaner slots available.") +} + +// Run all of the thread-local cleaner procedures. +// +// Intended to be called by the internals of a threading API at the end of a +// thread's lifetime. +run_thread_local_cleaners :: proc "odin" () { + for p in thread_local_cleaners { + if p == nil { + break + } + p() + } +} diff --git a/core/mem/alloc.odin b/core/mem/alloc.odin index acd77241f..e51d971e1 100644 --- a/core/mem/alloc.odin +++ b/core/mem/alloc.odin @@ -178,11 +178,11 @@ make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocato } @(require_results) make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) { - return runtime.make_dynamic_array(T, len, allocator, loc) + return runtime.make_dynamic_array_len_cap(T, len, len, allocator, loc) } @(require_results) make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #any_int cap: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) { - return runtime.make_dynamic_array(T, len, cap, allocator, loc) + return runtime.make_dynamic_array_len_cap(T, len, cap, allocator, loc) } @(require_results) make_map :: proc($T: typeid/map[$K]$E, #any_int cap: int = 1< (m: T, err: Allocator_Error) { diff --git a/core/mem/virtual/arena.odin b/core/mem/virtual/arena.odin index 80c231c31..79407d80d 100644 --- a/core/mem/virtual/arena.odin +++ b/core/mem/virtual/arena.odin @@ -49,6 +49,10 @@ arena_init_growing :: proc(arena: ^Arena, reserved: uint = DEFAULT_ARENA_GROWING arena.curr_block = memory_block_alloc(0, reserved, {}) or_return arena.total_used = 0 arena.total_reserved = arena.curr_block.reserved + + if arena.minimum_block_size == 0 { + arena.minimum_block_size = reserved + } return } diff --git a/core/mem/virtual/virtual.odin b/core/mem/virtual/virtual.odin index 4e53aba66..4afc33813 100644 --- a/core/mem/virtual/virtual.odin +++ b/core/mem/virtual/virtual.odin @@ -7,6 +7,11 @@ _ :: runtime DEFAULT_PAGE_SIZE := uint(4096) +@(init, private) +platform_memory_init :: proc() { + _platform_memory_init() +} + Allocator_Error :: mem.Allocator_Error @(require_results) diff --git a/core/mem/virtual/virtual_posix.odin b/core/mem/virtual/virtual_posix.odin index fbe89abfa..035763466 100644 --- a/core/mem/virtual/virtual_posix.odin +++ b/core/mem/virtual/virtual_posix.odin @@ -51,8 +51,10 @@ _protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) } _platform_memory_init :: proc() { - DEFAULT_PAGE_SIZE = posix.PAGE_SIZE - + // NOTE: `posix.PAGESIZE` due to legacy reasons could be wrong so we use `sysconf`. + size := posix.sysconf(._PAGESIZE) + DEFAULT_PAGE_SIZE = uint(max(size, posix.PAGESIZE)) + // is power of two assert(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0) } diff --git a/core/os/os2/allocators.odin b/core/os/os2/allocators.odin index 0ab3adfb2..ddfe230be 100644 --- a/core/os/os2/allocators.odin +++ b/core/os/os2/allocators.odin @@ -61,3 +61,8 @@ TEMP_ALLOCATOR_GUARD :: #force_inline proc(loc := #caller_location) -> (runtime. global_default_temp_allocator_index = (global_default_temp_allocator_index+1)%MAX_TEMP_ARENA_COUNT return tmp, loc } + +@(init, private) +init_thread_local_cleaner :: proc() { + runtime.add_thread_local_cleaner(temp_allocator_fini) +} diff --git a/core/os/os2/heap_linux.odin b/core/os/os2/heap_linux.odin index 11cf5ab41..e80bb3dee 100644 --- a/core/os/os2/heap_linux.odin +++ b/core/os/os2/heap_linux.odin @@ -1,10 +1,17 @@ //+private package os2 +import "base:runtime" + import "core:sys/linux" import "core:sync" import "core:mem" +// Use the experimental custom heap allocator (over calling `malloc` etc.). +// This is a switch because there are thread-safety problems that need to be fixed. +// See: https://github.com/odin-lang/Odin/issues/4161 +USE_EXPERIMENTAL_ALLOCATOR :: #config(OS2_LINUX_USE_EXPERIMENTAL_ALLOCATOR, false) + // NOTEs // // All allocations below DIRECT_MMAP_THRESHOLD exist inside of memory "Regions." A region @@ -139,6 +146,8 @@ Region :: struct { memory: [BLOCKS_PER_REGION]Allocation_Header, } +when USE_EXPERIMENTAL_ALLOCATOR { + _heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode, size, alignment: int, old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, mem.Allocator_Error) { @@ -219,6 +228,10 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode, return nil, nil } +} else { + _heap_allocator_proc :: runtime.heap_allocator_proc +} + heap_alloc :: proc(size: int) -> rawptr { if size >= DIRECT_MMAP_THRESHOLD { return _direct_mmap_alloc(size) diff --git a/core/os/os2/process.odin b/core/os/os2/process.odin index 1310af0d9..ce65987b0 100644 --- a/core/os/os2/process.odin +++ b/core/os/os2/process.odin @@ -166,15 +166,15 @@ Process_Info :: struct { This procedure obtains an information, specified by `selection` parameter of a process given by `pid`. - - Use `free_process_info` to free the memory allocated by this procedure. In - case the function returns an error it may only have been an error for one part - of the information and you would still need to call it to free the other parts. + + Use `free_process_info` to free the memory allocated by this procedure. The + `free_process_info` procedure needs to be called, even if this procedure + returned an error, as some of the fields may have been allocated. **Note**: The resulting information may or may contain the fields specified by the `selection` parameter. Always check whether the returned `Process_Info` struct has the required fields before checking the error code - returned by this function. + returned by this procedure. */ @(require_results) process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) { @@ -188,14 +188,14 @@ process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator: about a process that has been opened by the application, specified in the `process` parameter. - Use `free_process_info` to free the memory allocated by this procedure. In - case the function returns an error it may only have been an error for one part - of the information and you would still need to call it to free the other parts. + Use `free_process_info` to free the memory allocated by this procedure. The + `free_process_info` procedure needs to be called, even if this procedure + returned an error, as some of the fields may have been allocated. **Note**: The resulting information may or may contain the fields specified by the `selection` parameter. Always check whether the returned `Process_Info` struct has the required fields before checking the error code - returned by this function. + returned by this procedure. */ @(require_results) process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) { @@ -208,14 +208,14 @@ process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields, This procedure obtains the information, specified by `selection` parameter about the currently running process. - Use `free_process_info` to free the memory allocated by this procedure. In - case the function returns an error it may only have been an error for one part - of the information and you would still need to call it to free the other parts. + Use `free_process_info` to free the memory allocated by this procedure. The + `free_process_info` procedure needs to be called, even if this procedure + returned an error, as some of the fields may have been allocated. **Note**: The resulting information may or may contain the fields specified by the `selection` parameter. Always check whether the returned `Process_Info` struct has the required fields before checking the error code - returned by this function. + returned by this procedure. */ @(require_results) current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) { diff --git a/core/os/os2/process_windows.odin b/core/os/os2/process_windows.odin index 47fd62401..edb321509 100644 --- a/core/os/os2/process_windows.odin +++ b/core/os/os2/process_windows.odin @@ -93,34 +93,11 @@ read_memory_as_slice :: proc(h: win32.HANDLE, addr: rawptr, dest: []$T) -> (byte @(private="package") _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) { info.pid = pid - defer if err != nil { - free_process_info(info, allocator) - } - - // Data obtained from process snapshots - if selection >= {.PPid, .Priority} { - entry, entry_err := _process_entry_by_pid(info.pid) - if entry_err != nil { - err = General_Error.Not_Exist - return - } - if .PPid in selection { - info.fields += {.PPid} - info.ppid = int(entry.th32ParentProcessID) - } - if .Priority in selection { - info.fields += {.Priority} - info.priority = int(entry.pcPriClassBase) - } - } - if .Executable_Path in selection { // snap module - info.executable_path = _process_exe_by_pid(pid, allocator) or_return - info.fields += {.Executable_Path} - } - + // Note(flysand): Open the process handle right away to prevent some race + // conditions. Once the handle is open, the process will be kept alive by + // the OS. ph := win32.INVALID_HANDLE_VALUE - - if selection >= {.Command_Line, .Environment, .Working_Dir, .Username} { // need process handle + if selection >= {.Command_Line, .Environment, .Working_Dir, .Username} { ph = win32.OpenProcess( win32.PROCESS_QUERY_LIMITED_INFORMATION | win32.PROCESS_VM_READ, false, @@ -134,84 +111,15 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator defer if ph != win32.INVALID_HANDLE_VALUE { win32.CloseHandle(ph) } - - if selection >= {.Command_Line, .Environment, .Working_Dir} { // need peb - process_info_size: u32 - process_info: win32.PROCESS_BASIC_INFORMATION - status := win32.NtQueryInformationProcess(ph, .ProcessBasicInformation, &process_info, size_of(process_info), &process_info_size) - if status != 0 { - // TODO(flysand): There's probably a mismatch between NTSTATUS and - // windows userland error codes, I haven't checked. - err = Platform_Error(status) - return - } - if process_info.PebBaseAddress == nil { - // Not sure what the error is - err = General_Error.Unsupported - return - } - process_peb: win32.PEB - - _ = read_memory_as_struct(ph, process_info.PebBaseAddress, &process_peb) or_return - - process_params: win32.RTL_USER_PROCESS_PARAMETERS - _ = read_memory_as_struct(ph, process_peb.ProcessParameters, &process_params) or_return - - if selection >= {.Command_Line, .Command_Args} { - TEMP_ALLOCATOR_GUARD() - cmdline_w := make([]u16, process_params.CommandLine.Length, temp_allocator()) or_return - _ = read_memory_as_slice(ph, process_params.CommandLine.Buffer, cmdline_w) or_return - - if .Command_Line in selection { - info.command_line = win32_utf16_to_utf8(cmdline_w, allocator) or_return - info.fields += {.Command_Line} - } - if .Command_Args in selection { - info.command_args = _parse_command_line(raw_data(cmdline_w), allocator) or_return - info.fields += {.Command_Args} - } - } - if .Environment in selection { - TEMP_ALLOCATOR_GUARD() - env_len := process_params.EnvironmentSize / 2 - envs_w := make([]u16, env_len, temp_allocator()) or_return - _ = read_memory_as_slice(ph, process_params.Environment, envs_w) or_return - - info.environment = _parse_environment_block(raw_data(envs_w), allocator) or_return - info.fields += {.Environment} - } - if .Working_Dir in selection { - TEMP_ALLOCATOR_GUARD() - cwd_w := make([]u16, process_params.CurrentDirectoryPath.Length, temp_allocator()) or_return - _ = read_memory_as_slice(ph, process_params.CurrentDirectoryPath.Buffer, cwd_w) or_return - - info.working_dir = win32_utf16_to_utf8(cwd_w, allocator) or_return - info.fields += {.Working_Dir} - } - } - - if .Username in selection { - info.username = _get_process_user(ph, allocator) or_return - info.fields += {.Username} - } - err = nil - return -} - -@(private="package") -_process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) { - pid := process.pid - info.pid = pid - defer if err != nil { - free_process_info(info, allocator) - } - - // Data obtained from process snapshots - if selection >= {.PPid, .Priority} { // snap process + snapshot_process: if selection >= {.PPid, .Priority} { entry, entry_err := _process_entry_by_pid(info.pid) if entry_err != nil { - err = General_Error.Not_Exist - return + err = entry_err + if entry_err == General_Error.Not_Exist { + return + } else { + break snapshot_process + } } if .PPid in selection { info.fields += {.PPid} @@ -222,12 +130,18 @@ _process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields info.priority = int(entry.pcPriClassBase) } } - if .Executable_Path in selection { // snap module - info.executable_path = _process_exe_by_pid(pid, allocator) or_return + snapshot_modules: if .Executable_Path in selection { + exe_path: string + exe_path, err = _process_exe_by_pid(pid, allocator) + if _, ok := err.(runtime.Allocator_Error); ok { + return + } else if err != nil { + break snapshot_modules + } + info.executable_path = exe_path info.fields += {.Executable_Path} } - ph := win32.HANDLE(process.handle) - if selection >= {.Command_Line, .Environment, .Working_Dir} { // need peb + read_peb: if selection >= {.Command_Line, .Environment, .Working_Dir} { process_info_size: u32 process_info: win32.PROCESS_BASIC_INFORMATION status := win32.NtQueryInformationProcess(ph, .ProcessBasicInformation, &process_info, size_of(process_info), &process_info_size) @@ -235,25 +149,26 @@ _process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields // TODO(flysand): There's probably a mismatch between NTSTATUS and // windows userland error codes, I haven't checked. err = Platform_Error(status) - return + break read_peb } - if process_info.PebBaseAddress == nil { - // Not sure what the error is - err = General_Error.Unsupported - return - } - + assert(process_info.PebBaseAddress != nil) process_peb: win32.PEB - _ = read_memory_as_struct(ph, process_info.PebBaseAddress, &process_peb) or_return - + _, err = read_memory_as_struct(ph, process_info.PebBaseAddress, &process_peb) + if err != nil { + break read_peb + } process_params: win32.RTL_USER_PROCESS_PARAMETERS - _ = read_memory_as_struct(ph, process_peb.ProcessParameters, &process_params) or_return - + _, err = read_memory_as_struct(ph, process_peb.ProcessParameters, &process_params) + if err != nil { + break read_peb + } if selection >= {.Command_Line, .Command_Args} { TEMP_ALLOCATOR_GUARD() cmdline_w := make([]u16, process_params.CommandLine.Length, temp_allocator()) or_return - _ = read_memory_as_slice(ph, process_params.CommandLine.Buffer, cmdline_w) or_return - + _, err = read_memory_as_slice(ph, process_params.CommandLine.Buffer, cmdline_w) + if err != nil { + break read_peb + } if .Command_Line in selection { info.command_line = win32_utf16_to_utf8(cmdline_w, allocator) or_return info.fields += {.Command_Line} @@ -263,28 +178,147 @@ _process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields info.fields += {.Command_Args} } } - if .Environment in selection { TEMP_ALLOCATOR_GUARD() env_len := process_params.EnvironmentSize / 2 envs_w := make([]u16, env_len, temp_allocator()) or_return - _ = read_memory_as_slice(ph, process_params.Environment, envs_w) or_return - - info.environment = _parse_environment_block(raw_data(envs_w), allocator) or_return + _, err = read_memory_as_slice(ph, process_params.Environment, envs_w) + if err != nil { + break read_peb + } + info.environment = _parse_environment_block(raw_data(envs_w), allocator) or_return info.fields += {.Environment} } - if .Working_Dir in selection { TEMP_ALLOCATOR_GUARD() cwd_w := make([]u16, process_params.CurrentDirectoryPath.Length, temp_allocator()) or_return - _ = read_memory_as_slice(ph, process_params.CurrentDirectoryPath.Buffer, cwd_w) or_return - + _, err = read_memory_as_slice(ph, process_params.CurrentDirectoryPath.Buffer, cwd_w) + if err != nil { + break read_peb + } info.working_dir = win32_utf16_to_utf8(cwd_w, allocator) or_return info.fields += {.Working_Dir} } } - if .Username in selection { - info.username = _get_process_user(ph, allocator) or_return + read_username: if .Username in selection { + username: string + username, err = _get_process_user(ph, allocator) + if _, ok := err.(runtime.Allocator_Error); ok { + return + } else if err != nil { + break read_username + } + info.username = username + info.fields += {.Username} + } + err = nil + return +} + +@(private="package") +_process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) { + pid := process.pid + info.pid = pid + // Data obtained from process snapshots + snapshot_process: if selection >= {.PPid, .Priority} { + entry, entry_err := _process_entry_by_pid(info.pid) + if entry_err != nil { + err = entry_err + if entry_err == General_Error.Not_Exist { + return + } else { + break snapshot_process + } + } + if .PPid in selection { + info.fields += {.PPid} + info.ppid = int(entry.th32ParentProcessID) + } + if .Priority in selection { + info.fields += {.Priority} + info.priority = int(entry.pcPriClassBase) + } + } + snapshot_module: if .Executable_Path in selection { + exe_path: string + exe_path, err = _process_exe_by_pid(pid, allocator) + if _, ok := err.(runtime.Allocator_Error); ok { + return + } else if err != nil { + break snapshot_module + } + info.executable_path = exe_path + info.fields += {.Executable_Path} + } + ph := win32.HANDLE(process.handle) + read_peb: if selection >= {.Command_Line, .Environment, .Working_Dir} { + process_info_size: u32 + process_info: win32.PROCESS_BASIC_INFORMATION + status := win32.NtQueryInformationProcess(ph, .ProcessBasicInformation, &process_info, size_of(process_info), &process_info_size) + if status != 0 { + // TODO(flysand): There's probably a mismatch between NTSTATUS and + // windows userland error codes, I haven't checked. + err = Platform_Error(status) + return + } + assert(process_info.PebBaseAddress != nil) + process_peb: win32.PEB + _, err = read_memory_as_struct(ph, process_info.PebBaseAddress, &process_peb) + if err != nil { + break read_peb + } + process_params: win32.RTL_USER_PROCESS_PARAMETERS + _, err = read_memory_as_struct(ph, process_peb.ProcessParameters, &process_params) + if err != nil { + break read_peb + } + if selection >= {.Command_Line, .Command_Args} { + TEMP_ALLOCATOR_GUARD() + cmdline_w := make([]u16, process_params.CommandLine.Length, temp_allocator()) or_return + _, err = read_memory_as_slice(ph, process_params.CommandLine.Buffer, cmdline_w) + if err != nil { + break read_peb + } + if .Command_Line in selection { + info.command_line = win32_utf16_to_utf8(cmdline_w, allocator) or_return + info.fields += {.Command_Line} + } + if .Command_Args in selection { + info.command_args = _parse_command_line(raw_data(cmdline_w), allocator) or_return + info.fields += {.Command_Args} + } + } + if .Environment in selection { + TEMP_ALLOCATOR_GUARD() + env_len := process_params.EnvironmentSize / 2 + envs_w := make([]u16, env_len, temp_allocator()) or_return + _, err = read_memory_as_slice(ph, process_params.Environment, envs_w) + if err != nil { + break read_peb + } + info.environment = _parse_environment_block(raw_data(envs_w), allocator) or_return + info.fields += {.Environment} + } + if .Working_Dir in selection { + TEMP_ALLOCATOR_GUARD() + cwd_w := make([]u16, process_params.CurrentDirectoryPath.Length, temp_allocator()) or_return + _, err = read_memory_as_slice(ph, process_params.CurrentDirectoryPath.Buffer, cwd_w) + if err != nil { + break read_peb + } + info.working_dir = win32_utf16_to_utf8(cwd_w, allocator) or_return + info.fields += {.Working_Dir} + } + } + read_username: if .Username in selection { + username: string + username, err = _get_process_user(ph, allocator) + if _, ok := err.(runtime.Allocator_Error); ok { + return + } else if err != nil { + break read_username + } + info.username = username info.fields += {.Username} } err = nil @@ -294,15 +328,15 @@ _process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields @(private="package") _current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) { info.pid = get_pid() - defer if err != nil { - free_process_info(info, allocator) - } - - if selection >= {.PPid, .Priority} { // snap process + snapshot_process: if selection >= {.PPid, .Priority} { entry, entry_err := _process_entry_by_pid(info.pid) if entry_err != nil { - err = General_Error.Not_Exist - return + err = entry_err + if entry_err == General_Error.Not_Exist { + return + } else { + break snapshot_process + } } if .PPid in selection { info.fields += {.PPid} @@ -313,14 +347,16 @@ _current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime info.priority = int(entry.pcPriClassBase) } } - if .Executable_Path in selection { + module_filename: if .Executable_Path in selection { exe_filename_w: [256]u16 path_len := win32.GetModuleFileNameW(nil, raw_data(exe_filename_w[:]), len(exe_filename_w)) + assert(path_len > 0) info.executable_path = win32_utf16_to_utf8(exe_filename_w[:path_len], allocator) or_return info.fields += {.Executable_Path} } - if selection >= {.Command_Line, .Command_Args} { + command_line: if selection >= {.Command_Line, .Command_Args} { command_line_w := win32.GetCommandLineW() + assert(command_line_w != nil) if .Command_Line in selection { info.command_line = win32_wstring_to_utf8(command_line_w, allocator) or_return info.fields += {.Command_Line} @@ -330,14 +366,22 @@ _current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime info.fields += {.Command_Args} } } - if .Environment in selection { + read_environment: if .Environment in selection { env_block := win32.GetEnvironmentStringsW() + assert(env_block != nil) info.environment = _parse_environment_block(env_block, allocator) or_return info.fields += {.Environment} } - if .Username in selection { + read_username: if .Username in selection { process_handle := win32.GetCurrentProcess() - info.username = _get_process_user(process_handle, allocator) or_return + username: string + username, err = _get_process_user(process_handle, allocator) + if _, ok := err.(runtime.Allocator_Error); ok { + return + } else if err != nil { + break read_username + } + info.username = username info.fields += {.Username} } if .Working_Dir in selection { diff --git a/core/sys/darwin/CoreFoundation/CFString.odin b/core/sys/darwin/CoreFoundation/CFString.odin index 281f8b413..24485a494 100644 --- a/core/sys/darwin/CoreFoundation/CFString.odin +++ b/core/sys/darwin/CoreFoundation/CFString.odin @@ -1,7 +1,5 @@ package CoreFoundation -import "base:runtime" - foreign import CoreFoundation "system:CoreFoundation.framework" String :: distinct TypeRef // same as CFStringRef @@ -9,157 +7,157 @@ String :: distinct TypeRef // same as CFStringRef StringEncoding :: distinct u32 StringBuiltInEncodings :: enum StringEncoding { - MacRoman = 0, + MacRoman = 0, WindowsLatin1 = 0x0500, - ISOLatin1 = 0x0201, + ISOLatin1 = 0x0201, NextStepLatin = 0x0B01, - ASCII = 0x0600, - Unicode = 0x0100, - UTF8 = 0x08000100, + ASCII = 0x0600, + Unicode = 0x0100, + UTF8 = 0x08000100, NonLossyASCII = 0x0BFF, - UTF16 = 0x0100, + UTF16 = 0x0100, UTF16BE = 0x10000100, UTF16LE = 0x14000100, - UTF32 = 0x0c000100, - UTF32BE = 0x18000100, - UTF32LE = 0x1c000100, + UTF32 = 0x0c000100, + UTF32BE = 0x18000100, + UTF32LE = 0x1c000100, } StringEncodings :: enum Index { - MacJapanese = 1, - MacChineseTrad = 2, - MacKorean = 3, - MacArabic = 4, - MacHebrew = 5, - MacGreek = 6, - MacCyrillic = 7, - MacDevanagari = 9, - MacGurmukhi = 10, - MacGujarati = 11, - MacOriya = 12, - MacBengali = 13, - MacTamil = 14, - MacTelugu = 15, - MacKannada = 16, - MacMalayalam = 17, - MacSinhalese = 18, - MacBurmese = 19, - MacKhmer = 20, - MacThai = 21, - MacLaotian = 22, - MacGeorgian = 23, - MacArmenian = 24, - MacChineseSimp = 25, - MacTibetan = 26, - MacMongolian = 27, - MacEthiopic = 28, + MacJapanese = 1, + MacChineseTrad = 2, + MacKorean = 3, + MacArabic = 4, + MacHebrew = 5, + MacGreek = 6, + MacCyrillic = 7, + MacDevanagari = 9, + MacGurmukhi = 10, + MacGujarati = 11, + MacOriya = 12, + MacBengali = 13, + MacTamil = 14, + MacTelugu = 15, + MacKannada = 16, + MacMalayalam = 17, + MacSinhalese = 18, + MacBurmese = 19, + MacKhmer = 20, + MacThai = 21, + MacLaotian = 22, + MacGeorgian = 23, + MacArmenian = 24, + MacChineseSimp = 25, + MacTibetan = 26, + MacMongolian = 27, + MacEthiopic = 28, MacCentralEurRoman = 29, - MacVietnamese = 30, - MacExtArabic = 31, - MacSymbol = 33, - MacDingbats = 34, - MacTurkish = 35, - MacCroatian = 36, - MacIcelandic = 37, - MacRomanian = 38, - MacCeltic = 39, - MacGaelic = 40, - MacFarsi = 0x8C, - MacUkrainian = 0x98, - MacInuit = 0xEC, - MacVT100 = 0xFC, - MacHFS = 0xFF, - ISOLatin2 = 0x0202, - ISOLatin3 = 0x0203, - ISOLatin4 = 0x0204, - ISOLatinCyrillic = 0x0205, - ISOLatinArabic = 0x0206, - ISOLatinGreek = 0x0207, - ISOLatinHebrew = 0x0208, - ISOLatin5 = 0x0209, - ISOLatin6 = 0x020A, - ISOLatinThai = 0x020B, - ISOLatin7 = 0x020D, - ISOLatin8 = 0x020E, - ISOLatin9 = 0x020F, - ISOLatin10 = 0x0210, - DOSLatinUS = 0x0400, - DOSGreek = 0x0405, - DOSBalticRim = 0x0406, - DOSLatin1 = 0x0410, - DOSGreek1 = 0x0411, - DOSLatin2 = 0x0412, - DOSCyrillic = 0x0413, - DOSTurkish = 0x0414, - DOSPortuguese = 0x0415, - DOSIcelandic = 0x0416, - DOSHebrew = 0x0417, - DOSCanadianFrench = 0x0418, - DOSArabic = 0x0419, - DOSNordic = 0x041A, - DOSRussian = 0x041B, - DOSGreek2 = 0x041C, - DOSThai = 0x041D, - DOSJapanese = 0x0420, - DOSChineseSimplif = 0x0421, - DOSKorean = 0x0422, - DOSChineseTrad = 0x0423, - WindowsLatin2 = 0x0501, - WindowsCyrillic = 0x0502, - WindowsGreek = 0x0503, - WindowsLatin5 = 0x0504, - WindowsHebrew = 0x0505, - WindowsArabic = 0x0506, - WindowsBalticRim = 0x0507, - WindowsVietnamese = 0x0508, - WindowsKoreanJohab = 0x0510, - ANSEL = 0x0601, - JIS_X0201_76 = 0x0620, - JIS_X0208_83 = 0x0621, - JIS_X0208_90 = 0x0622, - JIS_X0212_90 = 0x0623, - JIS_C6226_78 = 0x0624, - ShiftJIS_X0213 = 0x0628, + MacVietnamese = 30, + MacExtArabic = 31, + MacSymbol = 33, + MacDingbats = 34, + MacTurkish = 35, + MacCroatian = 36, + MacIcelandic = 37, + MacRomanian = 38, + MacCeltic = 39, + MacGaelic = 40, + MacFarsi = 0x8C, + MacUkrainian = 0x98, + MacInuit = 0xEC, + MacVT100 = 0xFC, + MacHFS = 0xFF, + ISOLatin2 = 0x0202, + ISOLatin3 = 0x0203, + ISOLatin4 = 0x0204, + ISOLatinCyrillic = 0x0205, + ISOLatinArabic = 0x0206, + ISOLatinGreek = 0x0207, + ISOLatinHebrew = 0x0208, + ISOLatin5 = 0x0209, + ISOLatin6 = 0x020A, + ISOLatinThai = 0x020B, + ISOLatin7 = 0x020D, + ISOLatin8 = 0x020E, + ISOLatin9 = 0x020F, + ISOLatin10 = 0x0210, + DOSLatinUS = 0x0400, + DOSGreek = 0x0405, + DOSBalticRim = 0x0406, + DOSLatin1 = 0x0410, + DOSGreek1 = 0x0411, + DOSLatin2 = 0x0412, + DOSCyrillic = 0x0413, + DOSTurkish = 0x0414, + DOSPortuguese = 0x0415, + DOSIcelandic = 0x0416, + DOSHebrew = 0x0417, + DOSCanadianFrench = 0x0418, + DOSArabic = 0x0419, + DOSNordic = 0x041A, + DOSRussian = 0x041B, + DOSGreek2 = 0x041C, + DOSThai = 0x041D, + DOSJapanese = 0x0420, + DOSChineseSimplif = 0x0421, + DOSKorean = 0x0422, + DOSChineseTrad = 0x0423, + WindowsLatin2 = 0x0501, + WindowsCyrillic = 0x0502, + WindowsGreek = 0x0503, + WindowsLatin5 = 0x0504, + WindowsHebrew = 0x0505, + WindowsArabic = 0x0506, + WindowsBalticRim = 0x0507, + WindowsVietnamese = 0x0508, + WindowsKoreanJohab = 0x0510, + ANSEL = 0x0601, + JIS_X0201_76 = 0x0620, + JIS_X0208_83 = 0x0621, + JIS_X0208_90 = 0x0622, + JIS_X0212_90 = 0x0623, + JIS_C6226_78 = 0x0624, + ShiftJIS_X0213 = 0x0628, ShiftJIS_X0213_MenKuTen = 0x0629, - GB_2312_80 = 0x0630, - GBK_95 = 0x0631, - GB_18030_2000 = 0x0632, - KSC_5601_87 = 0x0640, - KSC_5601_92_Johab = 0x0641, - CNS_11643_92_P1 = 0x0651, - CNS_11643_92_P2 = 0x0652, - CNS_11643_92_P3 = 0x0653, - ISO_2022_JP = 0x0820, - ISO_2022_JP_2 = 0x0821, - ISO_2022_JP_1 = 0x0822, - ISO_2022_JP_3 = 0x0823, - ISO_2022_CN = 0x0830, - ISO_2022_CN_EXT = 0x0831, - ISO_2022_KR = 0x0840, - EUC_JP = 0x0920, - EUC_CN = 0x0930, - EUC_TW = 0x0931, - EUC_KR = 0x0940, - ShiftJIS = 0x0A01, - KOI8_R = 0x0A02, - Big5 = 0x0A03, - MacRomanLatin1 = 0x0A04, - HZ_GB_2312 = 0x0A05, - Big5_HKSCS_1999 = 0x0A06, - VISCII = 0x0A07, - KOI8_U = 0x0A08, - Big5_E = 0x0A09, - NextStepJapanese = 0x0B02, - EBCDIC_US = 0x0C01, - EBCDIC_CP037 = 0x0C02, - UTF7 = 0x04000100, - UTF7_IMAP = 0x0A10, - ShiftJIS_X0213_00 = 0x0628, // Deprecated. Use `ShiftJIS_X0213` instead. + GB_2312_80 = 0x0630, + GBK_95 = 0x0631, + GB_18030_2000 = 0x0632, + KSC_5601_87 = 0x0640, + KSC_5601_92_Johab = 0x0641, + CNS_11643_92_P1 = 0x0651, + CNS_11643_92_P2 = 0x0652, + CNS_11643_92_P3 = 0x0653, + ISO_2022_JP = 0x0820, + ISO_2022_JP_2 = 0x0821, + ISO_2022_JP_1 = 0x0822, + ISO_2022_JP_3 = 0x0823, + ISO_2022_CN = 0x0830, + ISO_2022_CN_EXT = 0x0831, + ISO_2022_KR = 0x0840, + EUC_JP = 0x0920, + EUC_CN = 0x0930, + EUC_TW = 0x0931, + EUC_KR = 0x0940, + ShiftJIS = 0x0A01, + KOI8_R = 0x0A02, + Big5 = 0x0A03, + MacRomanLatin1 = 0x0A04, + HZ_GB_2312 = 0x0A05, + Big5_HKSCS_1999 = 0x0A06, + VISCII = 0x0A07, + KOI8_U = 0x0A08, + Big5_E = 0x0A09, + NextStepJapanese = 0x0B02, + EBCDIC_US = 0x0C01, + EBCDIC_CP037 = 0x0C02, + UTF7 = 0x04000100, + UTF7_IMAP = 0x0A10, + ShiftJIS_X0213_00 = 0x0628, // Deprecated. Use `ShiftJIS_X0213` instead. } -@(link_prefix = "CF", default_calling_convention = "c") +@(link_prefix="CF", default_calling_convention="c") foreign CoreFoundation { // Copies the character contents of a string to a local C string buffer after converting the characters to a given encoding. StringGetCString :: proc(theString: String, buffer: [^]byte, bufferSize: Index, encoding: StringEncoding) -> b8 --- @@ -181,23 +179,16 @@ foreign CoreFoundation { STR :: StringMakeConstantString -StringCopyToOdinString :: proc( - theString: String, - allocator := context.allocator, -) -> ( - str: string, - ok: bool, -) #optional_ok { +StringCopyToOdinString :: proc(theString: String, allocator := context.allocator) -> (str: string, ok: bool) #optional_ok { length := StringGetLength(theString) max := StringGetMaximumSizeForEncoding(length, StringEncoding(StringBuiltInEncodings.UTF8)) buf, err := make([]byte, max, allocator) - if err != nil { return } - - raw_str := runtime.Raw_String { - data = raw_data(buf), + if err != nil { + return } - StringGetBytes(theString, {0, length}, StringEncoding(StringBuiltInEncodings.UTF8), 0, false, raw_data(buf), max, (^Index)(&raw_str.len)) - return transmute(string)raw_str, true + n: Index + StringGetBytes(theString, {0, length}, StringEncoding(StringBuiltInEncodings.UTF8), 0, false, raw_data(buf), Index(len(buf)), &n) + return string(buf[:n]), true } diff --git a/core/sys/unix/pthread_unix.odin b/core/sys/unix/pthread_unix.odin index 68a0859b4..26a21e629 100644 --- a/core/sys/unix/pthread_unix.odin +++ b/core/sys/unix/pthread_unix.odin @@ -27,6 +27,8 @@ foreign pthread { pthread_equal :: proc(a, b: pthread_t) -> b32 --- + pthread_detach :: proc(t: pthread_t) -> c.int --- + sched_get_priority_min :: proc(policy: c.int) -> c.int --- sched_get_priority_max :: proc(policy: c.int) -> c.int --- diff --git a/core/sys/windows/kernel32.odin b/core/sys/windows/kernel32.odin index 629ece3de..81db67185 100644 --- a/core/sys/windows/kernel32.odin +++ b/core/sys/windows/kernel32.odin @@ -400,6 +400,9 @@ foreign kernel32 { GlobalAlloc :: proc(flags: UINT, bytes: SIZE_T) -> LPVOID --- GlobalReAlloc :: proc(mem: LPVOID, bytes: SIZE_T, flags: UINT) -> LPVOID --- GlobalFree :: proc(mem: LPVOID) -> LPVOID --- + + GlobalLock :: proc(hMem: HGLOBAL) -> LPVOID --- + GlobalUnlock :: proc(hMem: HGLOBAL) -> BOOL --- ReadDirectoryChangesW :: proc( hDirectory: HANDLE, diff --git a/core/sys/windows/user32.odin b/core/sys/windows/user32.odin index f1232c680..e13dcd55f 100644 --- a/core/sys/windows/user32.odin +++ b/core/sys/windows/user32.odin @@ -66,7 +66,7 @@ foreign user32 { RemovePropW :: proc(hWnd: HWND, lpString: LPCWSTR) -> HANDLE --- EnumPropsW :: proc(hWnd: HWND, lpEnumFunc: PROPENUMPROCW) -> INT --- EnumPropsExW :: proc(hWnd: HWND, lpEnumFunc: PROPENUMPROCW, lParam: LPARAM) -> INT --- - GetMessageW :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT) -> BOOL --- + GetMessageW :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT) -> INT --- TranslateMessage :: proc(lpMsg: ^MSG) -> BOOL --- DispatchMessageW :: proc(lpMsg: ^MSG) -> LRESULT --- @@ -142,7 +142,7 @@ foreign user32 { AppendMenuW :: proc(hMenu: HMENU, uFlags: UINT, uIDNewItem: UINT_PTR, lpNewItem: LPCWSTR) -> BOOL --- GetMenu :: proc(hWnd: HWND) -> HMENU --- SetMenu :: proc(hWnd: HWND, hMenu: HMENU) -> BOOL --- - TrackPopupMenu :: proc(hMenu: HMENU, uFlags: UINT, x, y: INT, nReserved: INT, hWnd: HWND, prcRect: ^RECT) -> BOOL --- + TrackPopupMenu :: proc(hMenu: HMENU, uFlags: UINT, x, y: INT, nReserved: INT, hWnd: HWND, prcRect: ^RECT) -> INT --- RegisterWindowMessageW :: proc(lpString: LPCWSTR) -> UINT --- CreateAcceleratorTableW :: proc(paccel: LPACCEL, cAccel: INT) -> HACCEL --- @@ -305,6 +305,13 @@ foreign user32 { GetProcessWindowStation :: proc() -> HWINSTA --- GetUserObjectInformationW :: proc(hObj: HANDLE, nIndex: GetUserObjectInformationFlags, pvInfo: PVOID, nLength: DWORD, lpnLengthNeeded: LPDWORD) -> BOOL --- + + OpenClipboard :: proc(hWndNewOwner: HWND) -> BOOL --- + CloseClipboard :: proc() -> BOOL --- + GetClipboardData :: proc(uFormat: UINT) -> HANDLE --- + SetClipboardData :: proc(uFormat: UINT, hMem: HANDLE) -> HANDLE --- + IsClipboardFormatAvailable :: proc(format: UINT) -> BOOL --- + EmptyClipboard :: proc() -> BOOL --- } CreateWindowW :: #force_inline proc "system" ( @@ -746,3 +753,31 @@ WinEventFlag :: enum DWORD { SKIPOWNPROCESS = 1, INCONTEXT = 2, } + +// Standard Clipboard Formats +CF_TEXT :: 1 +CF_BITMAP :: 2 +CF_METAFILEPICT :: 3 +CF_SYLK :: 4 +CF_DIF :: 5 +CF_TIFF :: 6 +CF_OEMTEXT :: 7 +CF_DIB :: 8 +CF_PALETTE :: 9 +CF_PENDATA :: 10 +CF_RIFF :: 11 +CF_WAVE :: 12 +CF_UNICODETEXT :: 13 +CF_ENHMETAFILE :: 14 +CF_HDROP :: 15 +CF_LOCALE :: 16 +CF_DIBV5 :: 17 +CF_DSPBITMAP :: 0x0082 +CF_DSPENHMETAFILE :: 0x008E +CF_DSPMETAFILEPICT :: 0x0083 +CF_DSPTEXT :: 0x0081 +CF_GDIOBJFIRST :: 0x0300 +CF_GDIOBJLAST :: 0x03FF +CF_OWNERDISPLAY :: 0x0080 +CF_PRIVATEFIRST :: 0x0200 +CF_PRIVATELAST :: 0x02FF diff --git a/core/thread/thread_unix.odin b/core/thread/thread_unix.odin index f56454bfc..ddc47244c 100644 --- a/core/thread/thread_unix.odin +++ b/core/thread/thread_unix.odin @@ -2,6 +2,7 @@ // +private package thread +import "base:runtime" import "core:sync" import "core:sys/unix" import "core:time" @@ -55,7 +56,10 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread { // Here on Unix, we start the OS thread in a running state, and so we manually have it wait on a condition // variable above. We must perform that waiting BEFORE we select the context! context = _select_context_for_thread(init_context) - defer _maybe_destroy_default_temp_allocator(init_context) + defer { + _maybe_destroy_default_temp_allocator(init_context) + runtime.run_thread_local_cleaners() + } t.procedure(t) } @@ -65,6 +69,9 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread { sync.unlock(&t.mutex) if .Self_Cleanup in sync.atomic_load(&t.flags) { + res := unix.pthread_detach(t.unix_thread) + assert_contextless(res == 0) + t.unix_thread = {} // NOTE(ftphikari): It doesn't matter which context 'free' received, right? context = {} diff --git a/core/thread/thread_windows.odin b/core/thread/thread_windows.odin index 8da75a2d9..50a4e5fbc 100644 --- a/core/thread/thread_windows.odin +++ b/core/thread/thread_windows.odin @@ -3,6 +3,7 @@ package thread import "base:intrinsics" +import "base:runtime" import "core:sync" import win32 "core:sys/windows" @@ -39,7 +40,10 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread { // Here on Windows, the thread is created in a suspended state, and so we can select the context anywhere before the call // to t.procedure(). context = _select_context_for_thread(init_context) - defer _maybe_destroy_default_temp_allocator(init_context) + defer { + _maybe_destroy_default_temp_allocator(init_context) + runtime.run_thread_local_cleaners() + } t.procedure(t) } diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index 68c1e9d1e..f63c42ab9 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -263,7 +263,7 @@ gb_internal lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t) { if (is_type_simd_vector(src) && is_type_simd_vector(dst)) { res.value = LLVMBuildBitCast(p->builder, value.value, lb_type(p->module, t), ""); return res; - } else if (is_type_array_like(src) && is_type_simd_vector(dst)) { + } else if (is_type_array_like(src) && (is_type_simd_vector(dst) || is_type_integer_128bit(dst))) { unsigned align = cast(unsigned)gb_max(type_align_of(src), type_align_of(dst)); lbValue ptr = lb_address_from_load_or_generate_local(p, value); if (lb_try_update_alignment(ptr, align)) { diff --git a/tests/core/c/libc/test_core_libc_complex_pow.odin b/tests/core/c/libc/test_core_libc_complex_pow.odin index cd50c8f6a..e308cc171 100644 --- a/tests/core/c/libc/test_core_libc_complex_pow.odin +++ b/tests/core/c/libc/test_core_libc_complex_pow.odin @@ -69,18 +69,18 @@ test_libc_pow_binding :: proc(t: ^testing.T, $LIBC_COMPLEX:typeid, $F:typeid, po complex_power := LIBC_COMPLEX(complex(F(n), F(0.))) result := pow(complex_base, complex_power) switch n%%4 { - case 0: - expected_real = value - expected_imag = 0. - case 1: - expected_real = 0. - expected_imag = value - case 2: - expected_real = -value - expected_imag = 0. - case 3: - expected_real = 0. - expected_imag = -value + case 0: + expected_real = value + expected_imag = 0. + case 1: + expected_real = 0. + expected_imag = value + case 2: + expected_real = -value + expected_imag = 0. + case 3: + expected_real = 0. + expected_imag = -value } testing.expectf(t, isclose(t, expected_real, F(real(result)), rtol, atol), "ftype:%T, n:%v reldiff(%v, re(%v)) is greater than specified rtol:%e", F{}, n, expected_real, result, rtol) testing.expectf(t, isclose(t, expected_imag, F(imag(result)), rtol, atol), "ftype:%T, n:%v reldiff(%v, im(%v)) is greater than specified rtol:%e", F{}, n, expected_imag, result, rtol) diff --git a/tests/core/container/test_core_rbtree.odin b/tests/core/container/test_core_rbtree.odin index b686ef6dd..bdd23691c 100644 --- a/tests/core/container/test_core_rbtree.odin +++ b/tests/core/container/test_core_rbtree.odin @@ -187,7 +187,7 @@ validate_rbtree :: proc(t: ^testing.T, tree: ^$T/rb.Tree($Key, $Value)) { } verify_rbtree_propery_1 :: proc(t: ^testing.T, n: ^$N/rb.Node($Key, $Value)) { - testing.expect(t, rb.node_color(n) == .Black || rb.node_color(n) == .Red, "Property #1: Each node is either red or black.") + testing.expect(t, rb.node_color(n) == .Black || rb.node_color(n) == .Red, "Property #1: Each node is either red or black.") if n == nil { return } diff --git a/tests/core/encoding/json/test_core_json.odin b/tests/core/encoding/json/test_core_json.odin index 42ac9ce0f..f588d34fa 100644 --- a/tests/core/encoding/json/test_core_json.odin +++ b/tests/core/encoding/json/test_core_json.odin @@ -429,7 +429,7 @@ map_with_integer_keys :: proc(t: ^testing.T) { defer delete_map(my_map2) unmarshal_err := json.unmarshal(marshaled_data, &my_map2) - defer for key, item in my_map2 { + defer for _, item in my_map2 { runtime.delete_string(item) } testing.expectf(t, unmarshal_err == nil, "Expected `json.unmarshal` to return nil, got %v", unmarshal_err) diff --git a/tests/core/encoding/xml/test_core_xml.odin b/tests/core/encoding/xml/test_core_xml.odin index b29431e10..c0e4329bd 100644 --- a/tests/core/encoding/xml/test_core_xml.odin +++ b/tests/core/encoding/xml/test_core_xml.odin @@ -241,7 +241,7 @@ doc_to_string :: proc(doc: ^xml.Document) -> (result: string) { written += fmt.wprintf(writer, "[DOCTYPE] %v\n", doc.doctype.ident) if len(doc.doctype.rest) > 0 { - fmt.wprintf(writer, "\t%v\n", doc.doctype.rest) + fmt.wprintf(writer, "\t%v\n", doc.doctype.rest) } } @@ -250,10 +250,10 @@ doc_to_string :: proc(doc: ^xml.Document) -> (result: string) { } if doc.element_count > 0 { - fmt.wprintln(writer, " --- ") - print_element(writer, doc, 0) - fmt.wprintln(writer, " --- ") - } + fmt.wprintln(writer, " --- ") + print_element(writer, doc, 0) + fmt.wprintln(writer, " --- ") + } return written, .None } diff --git a/tests/core/sys/windows/test_clipboard.odin b/tests/core/sys/windows/test_clipboard.odin new file mode 100644 index 000000000..67fa6e4a2 --- /dev/null +++ b/tests/core/sys/windows/test_clipboard.odin @@ -0,0 +1,55 @@ +//+build windows +package test_core_sys_windows + +import "core:testing" +import win32 "core:sys/windows" +import "base:runtime" +import "core:strings" +import "core:mem" + +read_from_clipboard :: proc(wnd_handle: win32.HWND, allocator: runtime.Allocator) -> (result: string, ok: win32.BOOL) { + win32.IsClipboardFormatAvailable(win32.CF_TEXT) or_return + + win32.OpenClipboard(wnd_handle) or_return + defer win32.CloseClipboard() + + clipboard_data := win32.GetClipboardData(win32.CF_TEXT) + if clipboard_data != nil { + if cstr := cstring(win32.GlobalLock(win32.HGLOBAL(clipboard_data))); cstr != nil { + result = strings.clone_from_cstring(cstr, allocator) + ok = true + } + win32.GlobalUnlock(win32.HGLOBAL(clipboard_data)) + } + return +} + +write_to_clipboard :: proc(wnd_handle: win32.HWND, text: string) -> win32.BOOL { + win32.OpenClipboard(wnd_handle) or_return + defer win32.CloseClipboard() + win32.EmptyClipboard() + + h_mem := win32.HGLOBAL(win32.GlobalAlloc(win32.GMEM_MOVEABLE, len(text) + 1)) + if h_mem == nil {return false} + + cstr_dst := cast([^]u8)win32.GlobalLock(h_mem) + defer win32.GlobalUnlock(h_mem) + if cstr_dst == nil {return false} + + mem.copy(rawptr(cstr_dst), raw_data(text), len(text)) + cstr_dst[len(text)] = 0 + + win32.SetClipboardData(win32.CF_TEXT, win32.HANDLE(h_mem)) + return true +} + + +@(test) +verify_win32_clipboard :: proc(t: ^testing.T) { + ok1 := write_to_clipboard(nil, "Hello everynyan! OH MY GAH") + testing.expect_value(t, ok1, true) + + clipboard_content, ok2 := read_from_clipboard(nil, context.temp_allocator) + testing.expect_value(t, ok2, true) + testing.expect_value(t, clipboard_content, "Hello everynyan! OH MY GAH") +} diff --git a/tests/core/unicode/test_core_unicode.odin b/tests/core/unicode/test_core_unicode.odin index a1f6ac934..30a40b30b 100644 --- a/tests/core/unicode/test_core_unicode.odin +++ b/tests/core/unicode/test_core_unicode.odin @@ -16,8 +16,7 @@ run_test_cases :: proc(t: ^testing.T, test_cases: []Test_Case, loc := #caller_lo result, _, _ := utf8.grapheme_count(c.str) if !testing.expectf(t, result == c.expected_clusters, "(#% 4i) graphemes: %i != %i, %q %s", i, result, c.expected_clusters, c.str, c.str, - loc = loc) - { + loc = loc) { failed += 1 } } diff --git a/vendor/wgpu/README.md b/vendor/wgpu/README.md index 8b2c95b5e..1022e8541 100644 --- a/vendor/wgpu/README.md +++ b/vendor/wgpu/README.md @@ -11,8 +11,8 @@ Have a look at the `example/` directory for the rendering of a basic triangle. ## Getting the wgpu-native libraries For native support (not the browser), some libraries are required. Fortunately this is -extremely easy, just download them from the [releases on GitHub](https://github.com/gfx-rs/wgpu-native/releases/tag/v0.19.4.1), -the bindings are for v0.19.4.1 at the moment. +extremely easy, just download them from the [releases on GitHub](https://github.com/gfx-rs/wgpu-native/releases/tag/v22.1.0.1), +the bindings are for v22.1.0.1 at the moment. These are expected in the `lib` folder under the same name as they are released (just unzipped). By default it will look for a static release version (`wgpu-OS-ARCH-release.a|lib`), diff --git a/vendor/wgpu/examples/glfw/main.odin b/vendor/wgpu/examples/glfw/main.odin index bddd2c5f7..b57206371 100644 --- a/vendor/wgpu/examples/glfw/main.odin +++ b/vendor/wgpu/examples/glfw/main.odin @@ -158,15 +158,17 @@ frame :: proc "c" (dt: f32) { view = frame, loadOp = .Clear, storeOp = .Store, + depthSlice = wgpu.DEPTH_SLICE_UNDEFINED, clearValue = { 0, 1, 0, 1 }, }, }, ) - defer wgpu.RenderPassEncoderRelease(render_pass_encoder) wgpu.RenderPassEncoderSetPipeline(render_pass_encoder, state.pipeline) wgpu.RenderPassEncoderDraw(render_pass_encoder, vertexCount=3, instanceCount=1, firstVertex=0, firstInstance=0) + wgpu.RenderPassEncoderEnd(render_pass_encoder) + wgpu.RenderPassEncoderRelease(render_pass_encoder) command_buffer := wgpu.CommandEncoderFinish(command_encoder, nil) defer wgpu.CommandBufferRelease(command_buffer) diff --git a/vendor/wgpu/examples/sdl2/main.odin b/vendor/wgpu/examples/sdl2/main.odin index fa0a84bd5..58e357f7a 100644 --- a/vendor/wgpu/examples/sdl2/main.odin +++ b/vendor/wgpu/examples/sdl2/main.odin @@ -158,15 +158,17 @@ frame :: proc "c" (dt: f32) { view = frame, loadOp = .Clear, storeOp = .Store, + depthSlice = wgpu.DEPTH_SLICE_UNDEFINED, clearValue = { 0, 1, 0, 1 }, }, }, ) - defer wgpu.RenderPassEncoderRelease(render_pass_encoder) wgpu.RenderPassEncoderSetPipeline(render_pass_encoder, state.pipeline) wgpu.RenderPassEncoderDraw(render_pass_encoder, vertexCount=3, instanceCount=1, firstVertex=0, firstInstance=0) + wgpu.RenderPassEncoderEnd(render_pass_encoder) + wgpu.RenderPassEncoderRelease(render_pass_encoder) command_buffer := wgpu.CommandEncoderFinish(command_encoder, nil) defer wgpu.CommandBufferRelease(command_buffer) diff --git a/vendor/wgpu/lib/wgpu-windows-x86_64-release/wgpu_native.dll b/vendor/wgpu/lib/wgpu-windows-x86_64-release/wgpu_native.dll index 650260bfc..b8f782a2f 100644 Binary files a/vendor/wgpu/lib/wgpu-windows-x86_64-release/wgpu_native.dll and b/vendor/wgpu/lib/wgpu-windows-x86_64-release/wgpu_native.dll differ diff --git a/vendor/wgpu/lib/wgpu-windows-x86_64-release/wgpu_native.dll.lib b/vendor/wgpu/lib/wgpu-windows-x86_64-release/wgpu_native.dll.lib index b838c2695..864b7eb53 100644 Binary files a/vendor/wgpu/lib/wgpu-windows-x86_64-release/wgpu_native.dll.lib and b/vendor/wgpu/lib/wgpu-windows-x86_64-release/wgpu_native.dll.lib differ diff --git a/vendor/wgpu/lib/wgpu-windows-x86_64-release/wgpu_native.lib b/vendor/wgpu/lib/wgpu-windows-x86_64-release/wgpu_native.lib index ea4044d81..7c9f95c33 100644 Binary files a/vendor/wgpu/lib/wgpu-windows-x86_64-release/wgpu_native.lib and b/vendor/wgpu/lib/wgpu-windows-x86_64-release/wgpu_native.lib differ diff --git a/vendor/wgpu/lib/wgpu-windows-x86_64-release/wgpu_native.pdb b/vendor/wgpu/lib/wgpu-windows-x86_64-release/wgpu_native.pdb index b30090276..52865e174 100644 Binary files a/vendor/wgpu/lib/wgpu-windows-x86_64-release/wgpu_native.pdb and b/vendor/wgpu/lib/wgpu-windows-x86_64-release/wgpu_native.pdb differ diff --git a/vendor/wgpu/wgpu.js b/vendor/wgpu/wgpu.js index 4fe78c992..c100808e3 100644 --- a/vendor/wgpu/wgpu.js +++ b/vendor/wgpu/wgpu.js @@ -44,6 +44,7 @@ class WebGPUInterface { BlendFactor: ["zero", "one", "src", "one-minus-src", "src-alpha", "one-minus-src-alpha", "dst", "one-minus-dst", "dst-alpha", "one-minus-dst-alpha", "src-alpha-saturated", "constant", "one-minus-constant", ], PresentMode: ["fifo", "fifo-relaxed", "immediate", "mailbox", ], TextureAspect: ["all", "stencil-only", "depth-only"], + DeviceLostReason: [undefined, "unknown", "destroyed"], }; /** @type {WebGPUObjectManager<{}>} */ @@ -382,13 +383,19 @@ class WebGPUInterface { */ RenderPassColorAttachment(start) { const viewIdx = this.mem.loadPtr(start + 4); - const resolveTargetIdx = this.mem.loadPtr(start + 8); + const resolveTargetIdx = this.mem.loadPtr(start + 12); + + let depthSlice = this.mem.loadU32(start + 8); + if (depthSlice == 0xFFFFFFFF) { // DEPTH_SLICE_UNDEFINED. + depthSlice = undefined; + } return { view: viewIdx > 0 ? this.textureViews.get(viewIdx) : undefined, resolveTarget: resolveTargetIdx > 0 ? this.textureViews.get(resolveTargetIdx) : undefined, - loadOp: this.enumeration("LoadOp", start + 12), - storeOp: this.enumeration("StoreOp", start + 16), + depthSlice: depthSlice, + loadOp: this.enumeration("LoadOp", start + 16), + storeOp: this.enumeration("StoreOp", start + 20), clearValue: this.Color(start + 24), }; } @@ -950,14 +957,25 @@ class WebGPUInterface { /** * @param {number} adapterIdx - * @param {number} propertiesPtr + * @param {number} infoPtr */ - wgpuAdapterGetProperties: (adapterIdx, propertiesPtr) => { - this.assert(propertiesPtr != 0); - // Unknown adapter. - this.mem.storeI32(propertiesPtr + 28, 3); + wgpuAdapterGetInfo: (adapterIdx, infoPtr) => { + this.assert(infoPtr != 0); + // WebGPU backend. - this.mem.storeI32(propertiesPtr + 32, 2); + this.mem.storeI32(infoPtr + 20, 2); + // Unknown adapter. + this.mem.storeI32(infoPtr + 24, 3); + + // NOTE: I don't think getting the other fields in this struct is possible. + // `adapter.requestAdapterInfo` is deprecated. + }, + + /** + * @param {number} infoPtr + */ + wgpuAdapterInfoFreeMembers: (infoPtr) => { + // NOTE: nothing to free. }, /** @@ -970,50 +988,6 @@ class WebGPUInterface { return adapter.features.has(this.enums.FeatureName[featureInt]); }, - /** - * @param {number} adapterIdx - * @param {number} callbackPtr - * @param {0|number} userdata - */ - wgpuAdapterRequestAdapterInfo: async (adapterIdx, callbackPtr, userdata) => { - const adapter = this.adapters.get(adapterIdx); - const callback = this.mem.exports.__indirect_function_table.get(callbackPtr); - - const info = await adapter.requestAdapterInfo(); - - const addr = this.mem.exports.wgpu_alloc(16); - - const vendorLength = new TextEncoder().encode(info.vendor).length; - const vendorAddr = this.mem.exports.wgpu_alloc(vendorLength); - this.mem.storeString(vendorAddr, info.vendor); - this.mem.storeI32(addr + 0, vendorAddr); - - const architectureLength = new TextEncoder().encode(info.architecture).length; - const architectureAddr = this.mem.exports.wgpu_alloc(architectureLength); - this.mem.storeString(architectureAddr, info.architecture); - this.mem.storeI32(addr + 4, architectureAddr); - - - const deviceLength = new TextEncoder().encode(info.device).length; - const deviceAddr = this.mem.exports.wgpu_alloc(deviceLength); - this.mem.storeString(deviceAddr, info.device); - this.mem.storeI32(addr + 8, deviceAddr); - - - const descriptionLength = new TextEncoder().encode(info.description).length; - const descriptionAddr = this.mem.exports.wgpu_alloc(descriptionLength); - this.mem.storeString(descriptionAddr, info.description); - this.mem.storeI32(addr + 12, descriptionAddr); - - callback(addr, userdata); - - this.mem.exports.wgpu_free(descriptionAddr); - this.mem.exports.wgpu_free(deviceAddr); - this.mem.exports.wgpu_free(architectureAddr); - this.mem.exports.wgpu_free(vendorAddr); - this.mem.exports.wgpu_free(addr); - }, - /** * @param {number} adapterIdx * @param {0|number} descriptorPtr @@ -1040,14 +1014,69 @@ class WebGPUInterface { }; } + let device; let deviceIdx; try { - const device = await adapter.requestDevice(descriptor); + device = await adapter.requestDevice(descriptor); deviceIdx = this.devices.create(device); // NOTE: don't callback here, any errors that happen later will then be caught by the catch here. } catch (e) { - console.warn(e); - callback(1, null, null, userdata); + const messageLength = new TextEncoder().encode(e.message).length; + const messageAddr = this.mem.exports.wgpu_alloc(messageLength + 1); + this.mem.storeString(messageAddr, e.message); + + callback(1, null, messageAddr, userdata); + + this.mem.exports.wgpu_free(messageAddr); + } + + let callbacksPtr = descriptorPtr + 24 + this.mem.intSize; + + const deviceLostCallbackPtr = this.mem.loadPtr(callbacksPtr); + if (deviceLostCallbackPtr != 0) { + const deviceLostUserData = this.mem.loadPtr(callbacksPtr) + 4; + const deviceLostCallback = this.mem.exports.__indirect_function_table.get(deviceLostCallbackPtr); + + device.lost.then((info) => { + const reason = this.enums.DeviceLostReason.indexOf(info.reason); + + const messageLength = new TextEncoder().encode(info.message).length; + const messageAddr = this.mem.exports.wgpu_alloc(messageLength + 1); + this.mem.storeString(messageAddr, info.message); + + deviceLostCallback(reason, messageAddr, deviceLostUserData); + + this.mem.exports.wgpu_free(messageAddr); + }); + } + callbacksPtr += 8; + + // Skip over `nextInChain`. + callbacksPtr += 4; + + const uncapturedErrorCallbackPtr = this.mem.loadPtr(callbacksPtr); + if (uncapturedErrorCallbackPtr != 0) { + const uncapturedErrorUserData = this.mem.loadPtr(callbacksPtr + 4); + const uncapturedErrorCallback = this.mem.exports.__indirect_function_table.get(uncapturedErrorCallbackPtr); + + device.onuncapturederror = (ev) => { + let status = 4; // Unknown + if (ev.error instanceof GPUValidationError) { + status = 1; // Validation + } else if (ev.error instanceof GPUOutOfMemoryError) { + status = 2; // OutOfMemory + } else if (ev.error instanceof GPUInternalError) { + status = 3; // Internal + } + + const messageLength = new TextEncoder().encode(ev.error.message).length; + const messageAddr = this.mem.exports.wgpu_alloc(messageLength + 1); + this.mem.storeString(messageAddr, ev.error.message); + + uncapturedErrorCallback(status, messageAddr, uncapturedErrorUserData); + + this.mem.exports.wgpu_free(messageAddr); + }; } callback(0, deviceIdx, null, userdata); @@ -1918,29 +1947,6 @@ class WebGPUInterface { device.pushErrorScope(this.enums.ErrorFilter[filterInt]); }, - /** - * @param {number} deviceIdx - * @param {number} callbackPtr - * @param {number} userdata - */ - wgpuDeviceSetUncapturedErrorCallback: (deviceIdx, callbackPtr, userdata) => { - const device = this.devices.get(deviceIdx); - const callback = this.mem.exports.__indirect_function_table.get(callbackPtr); - - device.onuncapturederror = (ev) => { - console.warn(ev.error); - let status = 4; - if (error instanceof GPUValidationError) { - status = 1; - } else if (error instanceof GPUOutOfMemoryError) { - status = 2; - } else if (error instanceof GPUInternalError) { - status = 3; - } - callback(status, null, userdata); - }; - }, - ...this.devices.interface(true), /* ---------------------- Instance ---------------------- */ @@ -2646,23 +2652,23 @@ class WebGPUInterface { const formatStr = navigator.gpu.getPreferredCanvasFormat(); const format = this.enums.TextureFormat.indexOf(formatStr); - this.mem.storeUint(capabilitiesPtr + this.mem.intSize, 1); + this.mem.storeUint(capabilitiesPtr + 8, 1); const formatAddr = this.mem.exports.wgpu_alloc(4); this.mem.storeI32(formatAddr, format); - this.mem.storeI32(capabilitiesPtr + this.mem.intSize*2, formatAddr); + this.mem.storeI32(capabilitiesPtr + 8 + this.mem.intSize, formatAddr); // NOTE: present modes don't seem to actually do anything in JS, we can just give back a default FIFO though. - this.mem.storeUint(capabilitiesPtr + this.mem.intSize*3, 1); + this.mem.storeUint(capabilitiesPtr + 8 + this.mem.intSize*2, 1); const presentModesAddr = this.mem.exports.wgpu_alloc(4); this.mem.storeI32(presentModesAddr, 0); - this.mem.storeI32(capabilitiesPtr + this.mem.intSize*4, presentModesAddr); + this.mem.storeI32(capabilitiesPtr + 8 + this.mem.intSize*3, presentModesAddr); // Browser seems to support opaque (1) and premultiplied (2). - this.mem.storeUint(capabilitiesPtr + this.mem.intSize*5, 2); + this.mem.storeUint(capabilitiesPtr + 8 + this.mem.intSize*4, 2); const alphaModesAddr = this.mem.exports.wgpu_alloc(8); this.mem.storeI32(alphaModesAddr + 0, 1); // Opaque. this.mem.storeI32(alphaModesAddr + 4, 2); // premultiplied. - this.mem.storeI32(capabilitiesPtr + this.mem.intSize*6, alphaModesAddr); + this.mem.storeI32(capabilitiesPtr + 8 + this.mem.intSize*5, alphaModesAddr); }, /** @@ -2680,17 +2686,6 @@ class WebGPUInterface { // TODO: determine suboptimal and/or status. }, - /** - * @param {number} surfaceIdx - * @param {number} texturePtr - * @returns {number} - */ - wgpuSurfaceGetPreferredFormat: (surfaceIdx, adapterIdx) => { - const formatStr = navigator.gpu.getPreferredCanvasFormat(); - const format = this.enums.TextureFormat.indexOf(formatStr); - return format; - }, - /** * @param {number} surfaceIdx */ diff --git a/vendor/wgpu/wgpu.odin b/vendor/wgpu/wgpu.odin index af81dde56..ff52cdcb5 100644 --- a/vendor/wgpu/wgpu.odin +++ b/vendor/wgpu/wgpu.odin @@ -13,7 +13,7 @@ when ODIN_OS == .Windows { @(private) LIB :: "lib/wgpu-windows-" + ARCH + "-" + TYPE + "/wgpu_native" + EXT when !#exists(LIB) { - #panic("Could not find the compiled WGPU Native library at '" + #directory + LIB + "', these can be downloaded from https://github.com/gfx-rs/wgpu-native/releases/tag/v0.19.4.1, make sure to read the README at '" + #directory + "vendor/wgpu/README.md'") + #panic("Could not find the compiled WGPU Native library at '" + #directory + LIB + "', these can be downloaded from https://github.com/gfx-rs/wgpu-native/releases/tag/v22.1.0.1, make sure to read the README at '" + #directory + "vendor/wgpu/README.md'") } foreign import libwgpu { @@ -34,7 +34,7 @@ when ODIN_OS == .Windows { @(private) LIB :: "lib/wgpu-macos-" + ARCH + "-" + TYPE + "/libwgpu_native" + EXT when !#exists(LIB) { - #panic("Could not find the compiled WGPU Native library at '" + #directory + LIB + "', these can be downloaded from https://github.com/gfx-rs/wgpu-native/releases/tag/v0.19.4.1, make sure to read the README at '" + #directory + "vendor/wgpu/README.md'") + #panic("Could not find the compiled WGPU Native library at '" + #directory + LIB + "', these can be downloaded from https://github.com/gfx-rs/wgpu-native/releases/tag/v22.1.0.1, make sure to read the README at '" + #directory + "vendor/wgpu/README.md'") } foreign import libwgpu { @@ -49,7 +49,7 @@ when ODIN_OS == .Windows { @(private) LIB :: "lib/wgpu-linux-" + ARCH + "-" + TYPE + "/libwgpu_native" + EXT when !#exists(LIB) { - #panic("Could not find the compiled WGPU Native library at '" + #directory + LIB + "', these can be downloaded from https://github.com/gfx-rs/wgpu-native/releases/tag/v0.19.4.1, make sure to read the README at '" + #directory + "vendor/wgpu/README.md'") + #panic("Could not find the compiled WGPU Native library at '" + #directory + LIB + "', these can be downloaded from https://github.com/gfx-rs/wgpu-native/releases/tag/v22.1.0.1, make sure to read the README at '" + #directory + "vendor/wgpu/README.md'") } foreign import libwgpu { @@ -220,7 +220,8 @@ CullMode :: enum i32 { DeviceLostReason :: enum i32 { Undefined = 0x00000000, - Destroyed = 0x00000001, + Unknown = 0x00000001, + Destroyed = 0x00000002, } ErrorFilter :: enum i32 { @@ -264,6 +265,30 @@ FeatureName :: enum i32 { PipelineStatisticsQuery, StorageResourceBindingArray, PartiallyBoundBindingArray, + TextureFormat16bitNorm, + TextureCompressionAstcHdr, + // TODO: requires wgpu.h api change + // TimestampQueryInsidePasses, + MappablePrimaryBuffers = 0x0003000E, + BufferBindingArray, + UniformBufferAndStorageTextureArrayNonUniformIndexing, + // TODO: requires wgpu.h api change + // AddressModeClampToZero, + // AddressModeClampToBorder, + // PolygonModeLine, + // PolygonModePoint, + // ConservativeRasterization, + // ClearTexture, + // SprivShaderPassThrough, + // MultiView, + VertexAttribute64bit = 0x00030019, + TextureFormatNv12, + RayTracingAccelarationStructure, + RayQuery, + ShaderF64, + ShaderI16, + ShaderPrimitiveIndex, + ShaderEarlyDepthTest, } FilterMode :: enum i32 { @@ -520,6 +545,18 @@ TextureFormat :: enum i32 { ASTC12x10UnormSrgb = 0x0000005D, ASTC12x12Unorm = 0x0000005E, ASTC12x12UnormSrgb = 0x0000005F, + + // Native. + + // From FeatureName.TextureFormat16bitNorm + R16Unorm = 0x00030001, + R16Snorm, + Rg16Unorm, + Rg16Snorm, + Rgba16Unorm, + Rgba16Snorm, + // From FeatureName.TextureFormatNv12 + NV12, } TextureSampleType :: enum i32 { @@ -581,13 +618,13 @@ VertexStepMode :: enum i32 { VertexBufferNotUsed = 0x00000002, } -// WGSLFeatureName :: enum i32 { -// Undefined = 0x00000000, -// ReadonlyAndReadwriteStorageTextures = 0x00000001, -// Packed4x8IntegerDotProduct = 0x00000002, -// UnrestrictedPointerParameters = 0x00000003, -// PointerCompositeAccess = 0x00000004, -// } +WGSLFeatureName :: enum i32 { + Undefined = 0x00000000, + ReadonlyAndReadwriteStorageTextures = 0x00000001, + Packed4x8IntegerDotProduct = 0x00000002, + UnrestrictedPointerParameters = 0x00000003, + PointerCompositeAccess = 0x00000004, +} BufferUsage :: enum i32 { MapRead = 0x00000000, @@ -634,22 +671,18 @@ TextureUsage :: enum i32 { } TextureUsageFlags :: bit_set[TextureUsage; Flags] - -BufferMapAsyncCallback :: #type proc "c" (status: BufferMapAsyncStatus, /* NULLABLE */ userdata: rawptr) -ShaderModuleGetCompilationInfoCallback :: #type proc "c" (status: CompilationInfoRequestStatus, compilationInfo: ^CompilationInfo, /* NULLABLE */ userdata: rawptr) -DeviceCreateComputePipelineAsyncCallback :: #type proc "c" (status: CreatePipelineAsyncStatus, pipeline: ComputePipeline, message: cstring, /* NULLABLE */ userdata: rawptr) -DeviceCreateRenderPipelineAsyncCallback :: #type proc "c" (status: CreatePipelineAsyncStatus, pipeline: RenderPipeline, message: cstring, /* NULLABLE */ userdata: rawptr) +Proc :: distinct rawptr DeviceLostCallback :: #type proc "c" (reason: DeviceLostReason, message: cstring, userdata: rawptr) ErrorCallback :: #type proc "c" (type: ErrorType, message: cstring, userdata: rawptr) -Proc :: distinct rawptr - -QueueOnSubmittedWorkDoneCallback :: #type proc "c" (status: QueueWorkDoneStatus, /* NULLABLE */ userdata: rawptr) -InstanceRequestAdapterCallback :: #type proc "c" (status: RequestAdapterStatus, adapter: Adapter, message: cstring, /* NULLABLE */ userdata: rawptr) AdapterRequestDeviceCallback :: #type proc "c" (status: RequestDeviceStatus, device: Device, message: cstring, /* NULLABLE */ userdata: rawptr) - -// AdapterRequestAdapterInfoCallback :: #type proc "c" (adapterInfo: AdapterInfo, /* NULLABLE */ userdata: rawptr) +BufferMapAsyncCallback :: #type proc "c" (status: BufferMapAsyncStatus, /* NULLABLE */ userdata: rawptr) +DeviceCreateComputePipelineAsyncCallback :: #type proc "c" (status: CreatePipelineAsyncStatus, pipeline: ComputePipeline, message: cstring, /* NULLABLE */ userdata: rawptr) +DeviceCreateRenderPipelineAsyncCallback :: #type proc "c" (status: CreatePipelineAsyncStatus, pipeline: RenderPipeline, message: cstring, /* NULLABLE */ userdata: rawptr) +InstanceRequestAdapterCallback :: #type proc "c" (status: RequestAdapterStatus, adapter: Adapter, message: cstring, /* NULLABLE */ userdata: rawptr) +QueueOnSubmittedWorkDoneCallback :: #type proc "c" (status: QueueWorkDoneStatus, /* NULLABLE */ userdata: rawptr) +ShaderModuleGetCompilationInfoCallback :: #type proc "c" (status: CompilationInfoRequestStatus, compilationInfo: ^CompilationInfo, /* NULLABLE */ userdata: rawptr) ChainedStruct :: struct { next: ^ChainedStruct, @@ -661,28 +694,23 @@ ChainedStructOut :: struct { sType: SType, } -// AdapterInfo :: struct { -// next: ^ChainedStructOut, -// vendor: cstring, -// architecture: cstring, -// device: cstring, -// description: cstring, -// backendType: BackendType, -// adapterType: AdapterType, -// vendorID: u32, -// deviceID: u32, -// } - -AdapterProperties :: struct { +AdapterInfo :: struct { nextInChain: ^ChainedStructOut, - vendorID: u32, - vendorName: cstring, + vendor: cstring, architecture: cstring, - deviceID: u32, - name: cstring, - driverDescription: cstring, - adapterType: AdapterType, + device: cstring, + description: cstring, backendType: BackendType, + adapterType: AdapterType, + vendorID: u32, + deviceID: u32, +} +when ODIN_OS == .JS { + #assert(int(BackendType.WebGPU) == 2) + #assert(offset_of(AdapterInfo, backendType) == 20) + + #assert(int(AdapterType.Unknown) == 3) + #assert(offset_of(AdapterInfo, adapterType) == 24) } BindGroupEntry :: struct { @@ -943,6 +971,7 @@ StorageTextureBindingLayout :: struct { SurfaceCapabilities :: struct { nextInChain: ^ChainedStructOut, + usages: TextureUsageFlags, formatCount: uint, formats: /* const */ [^]TextureFormat `fmt:"v,formatCount"`, presentModeCount: uint, @@ -950,6 +979,16 @@ SurfaceCapabilities :: struct { alphaModeCount: uint, alphaModes: /* const */ [^]CompositeAlphaMode `fmt:"v,alphaModeCount"`, } +when ODIN_OS == .JS { + #assert(offset_of(SurfaceCapabilities, formatCount) == 8) + #assert(offset_of(SurfaceCapabilities, formats) == 8 + 1*size_of(int)) + + #assert(offset_of(SurfaceCapabilities, presentModeCount) == 8 + 2*size_of(int)) + #assert(offset_of(SurfaceCapabilities, presentModes) == 8 + 3*size_of(int)) + + #assert(offset_of(SurfaceCapabilities, alphaModeCount) == 8 + 4*size_of(int)) + #assert(offset_of(SurfaceCapabilities, alphaModes) == 8 + 5*size_of(int)) +} SurfaceConfiguration :: struct { nextInChain: ^ChainedStruct, @@ -1040,6 +1079,12 @@ TextureViewDescriptor :: struct { aspect: TextureAspect, } +UncapturedErrorCallbackInfo :: struct { + nextInChain: ^ChainedStruct, + callback: ErrorCallback, + userdata: rawptr, +} + VertexAttribute :: struct { format: VertexFormat, offset: u64, @@ -1120,12 +1165,21 @@ ProgrammableStageDescriptor :: struct { RenderPassColorAttachment :: struct { nextInChain: ^ChainedStruct, /* NULLABLE */ view: TextureView, - // depthSlice: u32, + depthSlice: u32, /* NULLABLE */ resolveTarget: TextureView, loadOp: LoadOp, storeOp: StoreOp, clearValue: Color, } +when ODIN_OS == .JS { + #assert(size_of(RenderPassColorAttachment) == 56) + #assert(offset_of(RenderPassColorAttachment, view) == 4) + #assert(offset_of(RenderPassColorAttachment, depthSlice) == 8) + #assert(offset_of(RenderPassColorAttachment, resolveTarget) == 12) + #assert(offset_of(RenderPassColorAttachment, loadOp) == 16) + #assert(offset_of(RenderPassColorAttachment, storeOp) == 20) + #assert(offset_of(RenderPassColorAttachment, clearValue) == 24) +} RequiredLimits :: struct { nextInChain: ^ChainedStruct, @@ -1194,6 +1248,10 @@ DeviceDescriptor :: struct { defaultQueue: QueueDescriptor, deviceLostCallback: DeviceLostCallback, deviceLostUserdata: rawptr, + uncapturedErrorCallbackInfo: UncapturedErrorCallbackInfo, +} +when ODIN_OS == .JS { + #assert(offset_of(DeviceDescriptor, deviceLostCallback) == 24 + size_of(int)) } RenderPassDescriptor :: struct { @@ -1245,16 +1303,18 @@ foreign libwgpu { // Methods of Adapter @(link_name="wgpuAdapterEnumerateFeatures") RawAdapterEnumerateFeatures :: proc(adapter: Adapter, features: [^]FeatureName) -> uint --- + @(link_name="wgpuAdapterGetInfo") + RawAdapterGetInfo :: proc(adapter: Adapter, info: ^AdapterInfo) --- @(link_name="wgpuAdapterGetLimits") RawAdapterGetLimits :: proc(adapter: Adapter, limits: ^SupportedLimits) -> b32 --- - @(link_name="wgpuAdapterGetProperties") - RawAdapterGetProperties :: proc(adapter: Adapter, properties: ^AdapterProperties) --- AdapterHasFeature :: proc(adapter: Adapter, feature: FeatureName) -> b32 --- - // AdapterRequestAdapterInfo :: proc(adapter: Adapter, callback: AdapterRequestAdapterInfoCallback, /* NULLABLE */ userdata: rawptr) --- AdapterRequestDevice :: proc(adapter: Adapter, /* NULLABLE */ descriptor: /* const */ ^DeviceDescriptor, callback: AdapterRequestDeviceCallback, /* NULLABLE */ userdata: rawptr = nil) --- AdapterReference :: proc(adapter: Adapter) --- AdapterRelease :: proc(adapter: Adapter) --- + // Procs of AdapterInfo + AdapterInfoFreeMembers :: proc(adapterInfo: AdapterInfo) --- + // Methods of BindGroup BindGroupSetLabel :: proc(bindGroup: BindGroup, label: cstring) --- BindGroupReference :: proc(bindGroup: BindGroup) --- @@ -1348,13 +1408,12 @@ foreign libwgpu { DevicePopErrorScope :: proc(device: Device, callback: ErrorCallback, userdata: rawptr) --- DevicePushErrorScope :: proc(device: Device, filter: ErrorFilter) --- DeviceSetLabel :: proc(device: Device, label: cstring) --- - DeviceSetUncapturedErrorCallback :: proc(device: Device, callback: ErrorCallback, userdata: rawptr) --- DeviceReference :: proc(device: Device) --- DeviceRelease :: proc(device: Device) --- // Methods of Instance InstanceCreateSurface :: proc(instance: Instance, descriptor: /* const */ ^SurfaceDescriptor) -> Surface --- - // InstanceHasWGSLLanguageFeature :: proc(instance: Instance, feature: WGSLFeatureName) -> b32 --- + InstanceHasWGSLLanguageFeature :: proc(instance: Instance, feature: WGSLFeatureName) -> b32 --- InstanceProcessEvents :: proc(instance: Instance) --- InstanceRequestAdapter :: proc(instance: Instance, /* NULLABLE */ options: /* const */ ^RequestAdapterOptions, callback: InstanceRequestAdapterCallback, /* NULLABLE */ userdata: rawptr = nil) --- InstanceReference :: proc(instance: Instance) --- @@ -1455,9 +1514,8 @@ foreign libwgpu { RawSurfaceGetCapabilities :: proc(surface: Surface, adapter: Adapter, capabilities: ^SurfaceCapabilities) --- @(link_name="wgpuSurfaceGetCurrentTexture") RawSurfaceGetCurrentTexture :: proc(surface: Surface, surfaceTexture: ^SurfaceTexture) --- - SurfaceGetPreferredFormat :: proc(surface: Surface, adapter: Adapter) -> TextureFormat --- SurfacePresent :: proc(surface: Surface) --- - // SurfaceSetLabel :: proc(surface: Surface, label: cstring) --- + SurfaceSetLabel :: proc(surface: Surface, label: cstring) --- SurfaceUnconfigure :: proc(surface: Surface) --- SurfaceReference :: proc(surface: Surface) --- SurfaceRelease :: proc(surface: Surface) --- @@ -1500,8 +1558,8 @@ AdapterGetLimits :: proc(adapter: Adapter) -> (limits: SupportedLimits, ok: bool return } -AdapterGetProperties :: proc(adapter: Adapter) -> (properties: AdapterProperties) { - RawAdapterGetProperties(adapter, &properties) +AdapterGetInfo :: proc(adapter: Adapter) -> (info: AdapterInfo) { + RawAdapterGetInfo(adapter, &info) return } @@ -1634,8 +1692,8 @@ SurfaceGetCurrentTexture :: proc(surface: Surface) -> (surface_texture: SurfaceT // WGPU Native bindings -BINDINGS_VERSION :: [4]u8{0, 19, 4, 1} -BINDINGS_VERSION_STRING :: "0.19.4.1" +BINDINGS_VERSION :: [4]u8{22, 1, 0, 1} +BINDINGS_VERSION_STRING :: "22.1.0.1" when ODIN_OS != .JS { @(private="file", init)