Browse Source

Merge branch 'master' of https://github.com/avanspector/Odin

avanspector 11 months ago
parent
commit
3135c89a0a

+ 21 - 21
.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"

+ 34 - 0
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()
+	}
+}

+ 2 - 2
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<<runtime.MAP_MIN_LOG2_CAPACITY, allocator := context.allocator, loc := #caller_location) -> (m: T, err: Allocator_Error) {

+ 4 - 0
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
 }
 

+ 5 - 0
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)

+ 4 - 2
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)
 }

+ 5 - 0
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)
+}

+ 13 - 0
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)

+ 13 - 13
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) {

+ 133 - 89
core/os/os2/process_windows.odin

@@ -93,16 +93,33 @@ 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)
+	// 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} {
+		ph = win32.OpenProcess(
+			win32.PROCESS_QUERY_LIMITED_INFORMATION | win32.PROCESS_VM_READ,
+			false,
+			u32(pid),
+		)
+		if ph == win32.INVALID_HANDLE_VALUE {
+			err = _get_platform_error()
+			return
+		}
 	}
-
-	// Data obtained from process snapshots
-	if selection >= {.PPid, .Priority} {
+	defer if ph != win32.INVALID_HANDLE_VALUE {
+		win32.CloseHandle(ph)
+	}
+	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}
@@ -113,29 +130,18 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
 			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}
-	}
-
-	ph := win32.INVALID_HANDLE_VALUE
-
-	if selection >= {.Command_Line, .Environment, .Working_Dir, .Username} { // need process handle
-		ph = win32.OpenProcess(
-			win32.PROCESS_QUERY_LIMITED_INFORMATION | win32.PROCESS_VM_READ,
-			false,
-			u32(pid),
-		)
-		if ph == win32.INVALID_HANDLE_VALUE {
-			err = _get_platform_error()
+	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}
 	}
-	defer if ph != win32.INVALID_HANDLE_VALUE {
-		win32.CloseHandle(ph)
-	}
-
-	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)
@@ -143,25 +149,26 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
 			// 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
+			break read_peb
 		}
+		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}
@@ -175,23 +182,33 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
 			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
-
+			_, 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
@@ -202,16 +219,16 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
 _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 +239,19 @@ _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_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)
-	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)
@@ -237,23 +261,24 @@ _process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields
 			err = Platform_Error(status)
 			return
 		}
-		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 +288,37 @@ _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
-
+			_, 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
@@ -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 {

+ 143 - 152
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
 }

+ 2 - 0
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 ---
 

+ 3 - 0
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,

+ 37 - 2
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

+ 8 - 1
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 = {}

+ 5 - 1
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)
 		}

+ 1 - 1
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)) {

+ 12 - 12
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)

+ 1 - 1
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
 	}

+ 1 - 1
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)

+ 5 - 5
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
 	}

+ 55 - 0
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")
+}

+ 1 - 2
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
 		}
 	}

+ 2 - 2
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`),

+ 3 - 1
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)

+ 3 - 1
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)

BIN
vendor/wgpu/lib/wgpu-windows-x86_64-release/wgpu_native.dll


BIN
vendor/wgpu/lib/wgpu-windows-x86_64-release/wgpu_native.dll.lib


BIN
vendor/wgpu/lib/wgpu-windows-x86_64-release/wgpu_native.lib


BIN
vendor/wgpu/lib/wgpu-windows-x86_64-release/wgpu_native.pdb


+ 91 - 96
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
 			 */

+ 111 - 53
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)