Merge branch 'master' of github.com:odin-lang/Odin into posix-linux

This commit is contained in:
Isaac Andrade
2024-08-27 18:51:58 -06:00
67 changed files with 3045 additions and 2704 deletions
+2
View File
@@ -219,6 +219,8 @@ type_map_cell_info :: proc($T: typeid) -> ^runtime.Map_Cell_Info ---
type_convert_variants_to_pointers :: proc($T: typeid) -> typeid where type_is_union(T) --- type_convert_variants_to_pointers :: proc($T: typeid) -> typeid where type_is_union(T) ---
type_merge :: proc($U, $V: typeid) -> typeid where type_is_union(U), type_is_union(V) --- type_merge :: proc($U, $V: typeid) -> typeid where type_is_union(U), type_is_union(V) ---
type_has_shared_fields :: proc($U, $V: typeid) -> bool typeid where type_is_struct(U), type_is_struct(V) ---
constant_utf16_cstring :: proc($literal: string) -> [^]u16 --- constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---
constant_log2 :: proc($v: $T) -> T where type_is_integer(T) --- constant_log2 :: proc($v: $T) -> T where type_is_integer(T) ---
+34
View File
@@ -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()
}
}
+8 -3
View File
@@ -11,7 +11,7 @@ Write a UUID in the 8-4-4-4-12 format.
This procedure performs error checking with every byte written. This procedure performs error checking with every byte written.
If you can guarantee beforehand that your stream has enough space to hold the If you can guarantee beforehand that your stream has enough space to hold the
UUID (32 bytes), then it is better to use `unsafe_write` instead as that will UUID (36 bytes), then it is better to use `unsafe_write` instead as that will
be faster. be faster.
Inputs: Inputs:
@@ -106,7 +106,7 @@ Convert a UUID to a string in the 8-4-4-4-12 format.
Inputs: Inputs:
- id: The identifier to convert. - id: The identifier to convert.
- buffer: A byte buffer to store the result. Must be at least 32 bytes large. - buffer: A byte buffer to store the result. Must be at least 36 bytes large.
- loc: The caller location for debugging purposes (default: #caller_location) - loc: The caller location for debugging purposes (default: #caller_location)
Returns: Returns:
@@ -119,7 +119,11 @@ to_string_buffer :: proc(
) -> ( ) -> (
str: string, str: string,
) { ) {
assert(len(buffer) >= EXPECTED_LENGTH, "The buffer provided is not at least 32 bytes large.", loc) assert(
len(buffer) >= EXPECTED_LENGTH,
"The buffer provided is not at least 36 bytes large.",
loc,
)
builder := strings.builder_from_bytes(buffer) builder := strings.builder_from_bytes(buffer)
unsafe_write(strings.to_writer(&builder), id) unsafe_write(strings.to_writer(&builder), id)
return strings.to_string(builder) return strings.to_string(builder)
@@ -129,3 +133,4 @@ to_string :: proc {
to_string_allocated, to_string_allocated,
to_string_buffer, to_string_buffer,
} }
+2
View File
@@ -1393,6 +1393,7 @@ expand_grayscale :: proc(img: ^Image, allocator := context.allocator) -> (ok: bo
for p in inp { for p in inp {
out[0].rgb = p.r // Gray component. out[0].rgb = p.r // Gray component.
out[0].a = p.g // Alpha component. out[0].a = p.g // Alpha component.
out = out[1:]
} }
case: case:
@@ -1417,6 +1418,7 @@ expand_grayscale :: proc(img: ^Image, allocator := context.allocator) -> (ok: bo
for p in inp { for p in inp {
out[0].rgb = p.r // Gray component. out[0].rgb = p.r // Gray component.
out[0].a = p.g // Alpha component. out[0].a = p.g // Alpha component.
out = out[1:]
} }
case: case:
+2 -2
View File
@@ -178,11 +178,11 @@ make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocato
} }
@(require_results) @(require_results)
make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) { 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) @(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) { 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) @(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) { 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) {
+18 -14
View File
@@ -128,33 +128,37 @@ _get_dns_records_os :: proc(hostname: string, type: DNS_Record_Type, allocator :
append(&recs, record) append(&recs, record)
case .SRV: case .SRV:
target := strings.clone(string(r.Data.SRV.pNameTarget)) // The target hostname/address that the service can be found on
priority := int(r.Data.SRV.wPriority)
weight := int(r.Data.SRV.wWeight)
port := int(r.Data.SRV.wPort)
// NOTE(tetra): Srv record name should be of the form '_servicename._protocol.hostname' // NOTE(tetra): Srv record name should be of the form '_servicename._protocol.hostname'
// The record name is the name of the record. // The record name is the name of the record.
// Not to be confused with the _target_ of the record, which is--in combination with the port--what we're looking up // Not to be confused with the _target_ of the record, which is--in combination with the port--what we're looking up
// by making this request in the first place. // by making this request in the first place.
// NOTE(Jeroen): Service Name and Protocol Name can probably just be string slices into the record name. service_name, protocol_name: string
// It's already cloned, after all. I wouldn't put them on the temp allocator like this.
parts := strings.split_n(base_record.record_name, ".", 3, context.temp_allocator) s := base_record.record_name
if len(parts) != 3 { i := strings.index_byte(s, '.')
if i > -1 {
service_name = s[:i]
s = s[len(service_name) + 1:]
} else {
continue
}
i = strings.index_byte(s, '.')
if i > -1 {
protocol_name = s[:i]
} else {
continue continue
} }
service_name, protocol_name := parts[0], parts[1]
append(&recs, DNS_Record_SRV { append(&recs, DNS_Record_SRV {
base = base_record, base = base_record,
target = target, target = strings.clone(string(r.Data.SRV.pNameTarget)), // The target hostname/address that the service can be found on
port = port, port = int(r.Data.SRV.wPort),
service_name = service_name, service_name = service_name,
protocol_name = protocol_name, protocol_name = protocol_name,
priority = priority, priority = int(r.Data.SRV.wPriority),
weight = weight, weight = int(r.Data.SRV.wWeight),
}) })
} }
+5
View File
@@ -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 global_default_temp_allocator_index = (global_default_temp_allocator_index+1)%MAX_TEMP_ARENA_COUNT
return tmp, loc return tmp, loc
} }
@(init, private)
init_thread_local_cleaner :: proc() {
runtime.add_thread_local_cleaner(temp_allocator_fini)
}
-3
View File
@@ -125,9 +125,6 @@ read_entire_file_from_file :: proc(f: ^File, allocator: runtime.Allocator) -> (d
has_size = true has_size = true
size = int(size64) size = int(size64)
} }
} else if serr != .No_Size {
err = serr
return
} }
if has_size && size > 0 { if has_size && size > 0 {
+13 -12
View File
@@ -167,14 +167,14 @@ Process_Info :: struct {
This procedure obtains an information, specified by `selection` parameter of This procedure obtains an information, specified by `selection` parameter of
a process given by `pid`. a process given by `pid`.
Use `free_process_info` to free the memory allocated by this procedure. In Use `free_process_info` to free the memory allocated by this procedure. The
case the function returns an error it may only have been an error for one part `free_process_info` procedure needs to be called, even if this procedure
of the information and you would still need to call it to free the other parts. returned an error, as some of the fields may have been allocated.
**Note**: The resulting information may or may contain the fields specified **Note**: The resulting information may or may contain the fields specified
by the `selection` parameter. Always check whether the returned by the `selection` parameter. Always check whether the returned
`Process_Info` struct has the required fields before checking the error code `Process_Info` struct has the required fields before checking the error code
returned by this function. returned by this procedure.
*/ */
@(require_results) @(require_results)
process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) { 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 about a process that has been opened by the application, specified in
the `process` parameter. the `process` parameter.
Use `free_process_info` to free the memory allocated by this procedure. In Use `free_process_info` to free the memory allocated by this procedure. The
case the function returns an error it may only have been an error for one part `free_process_info` procedure needs to be called, even if this procedure
of the information and you would still need to call it to free the other parts. returned an error, as some of the fields may have been allocated.
**Note**: The resulting information may or may contain the fields specified **Note**: The resulting information may or may contain the fields specified
by the `selection` parameter. Always check whether the returned by the `selection` parameter. Always check whether the returned
`Process_Info` struct has the required fields before checking the error code `Process_Info` struct has the required fields before checking the error code
returned by this function. returned by this procedure.
*/ */
@(require_results) @(require_results)
process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) { 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 This procedure obtains the information, specified by `selection` parameter
about the currently running process. about the currently running process.
Use `free_process_info` to free the memory allocated by this procedure. In Use `free_process_info` to free the memory allocated by this procedure. The
case the function returns an error it may only have been an error for one part `free_process_info` procedure needs to be called, even if this procedure
of the information and you would still need to call it to free the other parts. returned an error, as some of the fields may have been allocated.
**Note**: The resulting information may or may contain the fields specified **Note**: The resulting information may or may contain the fields specified
by the `selection` parameter. Always check whether the returned by the `selection` parameter. Always check whether the returned
`Process_Info` struct has the required fields before checking the error code `Process_Info` struct has the required fields before checking the error code
returned by this function. returned by this procedure.
*/ */
@(require_results) @(require_results)
current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) { current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) {
@@ -305,6 +305,7 @@ Process_Desc :: struct {
// A slice of strings, each having the format `KEY=VALUE` representing the // A slice of strings, each having the format `KEY=VALUE` representing the
// full environment that the child process will receive. // full environment that the child process will receive.
// In case this slice is `nil`, the current process' environment is used. // In case this slice is `nil`, the current process' environment is used.
// NOTE(laytan): maybe should be `Maybe([]string)` so you can do `nil` == current env, empty == empty/no env.
env: []string, env: []string,
// The `stderr` handle to give to the child process. It can be either a file // The `stderr` handle to give to the child process. It can be either a file
// or a writeable end of a pipe. Passing `nil` will shut down the process' // or a writeable end of a pipe. Passing `nil` will shut down the process'
+5 -11
View File
@@ -490,7 +490,6 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
if errno = linux.pipe2(&child_pipe_fds, {.CLOEXEC}); errno != .NONE { if errno = linux.pipe2(&child_pipe_fds, {.CLOEXEC}); errno != .NONE {
return process, _get_platform_error(errno) return process, _get_platform_error(errno)
} }
defer linux.close(child_pipe_fds[WRITE])
defer linux.close(child_pipe_fds[READ]) defer linux.close(child_pipe_fds[READ])
@@ -508,6 +507,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
// //
pid: linux.Pid pid: linux.Pid
if pid, errno = linux.fork(); errno != .NONE { if pid, errno = linux.fork(); errno != .NONE {
linux.close(child_pipe_fds[WRITE])
return process, _get_platform_error(errno) return process, _get_platform_error(errno)
} }
@@ -573,25 +573,19 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
write_errno_to_parent_and_abort(child_pipe_fds[WRITE], errno) write_errno_to_parent_and_abort(child_pipe_fds[WRITE], errno)
} }
success_byte: [1]u8
linux.write(child_pipe_fds[WRITE], success_byte[:])
errno = linux.execveat(exe_fd, "", &cargs[0], env, {.AT_EMPTY_PATH}) errno = linux.execveat(exe_fd, "", &cargs[0], env, {.AT_EMPTY_PATH})
// NOTE: we can't tell the parent about this failure because we already wrote the success byte.
// So if this happens the user will just see the process failed when they call process_wait.
assert(errno != nil) assert(errno != nil)
intrinsics.trap() write_errno_to_parent_and_abort(child_pipe_fds[WRITE], errno)
} }
linux.close(child_pipe_fds[WRITE])
process.pid = int(pid) process.pid = int(pid)
n: int
child_byte: [1]u8 child_byte: [1]u8
errno = .EINTR errno = .EINTR
for errno == .EINTR { for errno == .EINTR {
n, errno = linux.read(child_pipe_fds[READ], child_byte[:]) _, errno = linux.read(child_pipe_fds[READ], child_byte[:])
} }
// If the read failed, something weird happened. Do not return the read // If the read failed, something weird happened. Do not return the read
+7 -10
View File
@@ -139,20 +139,22 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
err = _get_platform_error() err = _get_platform_error()
return return
} }
defer posix.close(pipe[WRITE])
defer posix.close(pipe[READ]) defer posix.close(pipe[READ])
if posix.fcntl(pipe[READ], .SETFD, i32(posix.FD_CLOEXEC)) == -1 { if posix.fcntl(pipe[READ], .SETFD, i32(posix.FD_CLOEXEC)) == -1 {
posix.close(pipe[WRITE])
err = _get_platform_error() err = _get_platform_error()
return return
} }
if posix.fcntl(pipe[WRITE], .SETFD, i32(posix.FD_CLOEXEC)) == -1 { if posix.fcntl(pipe[WRITE], .SETFD, i32(posix.FD_CLOEXEC)) == -1 {
posix.close(pipe[WRITE])
err = _get_platform_error() err = _get_platform_error()
return return
} }
switch pid := posix.fork(); pid { switch pid := posix.fork(); pid {
case -1: case -1:
posix.close(pipe[WRITE])
err = _get_platform_error() err = _get_platform_error()
return return
@@ -179,25 +181,20 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
if posix.chdir(cwd) != .OK { abort(pipe[WRITE]) } if posix.chdir(cwd) != .OK { abort(pipe[WRITE]) }
} }
ok := u8(0)
posix.write(pipe[WRITE], &ok, 1)
res := posix.execve(strings.to_cstring(&exe_builder), raw_data(cmd), env) res := posix.execve(strings.to_cstring(&exe_builder), raw_data(cmd), env)
// NOTE: we can't tell the parent about this failure because we already wrote the success byte.
// So if this happens the user will just see the process failed when they call process_wait.
assert(res == -1) assert(res == -1)
runtime.trap() abort(pipe[WRITE])
case: case:
posix.close(pipe[WRITE])
errno: posix.Errno errno: posix.Errno
for { for {
errno_byte: u8 errno_byte: u8
switch posix.read(pipe[READ], &errno_byte, 1) { switch posix.read(pipe[READ], &errno_byte, 1) {
case 1: case 1:
errno = posix.Errno(errno_byte) errno = posix.Errno(errno_byte)
case: case -1:
errno = posix.errno() errno = posix.errno()
if errno == .EINTR { if errno == .EINTR {
continue continue
+182 -138
View File
@@ -93,34 +93,11 @@ read_memory_as_slice :: proc(h: win32.HANDLE, addr: rawptr, dest: []$T) -> (byte
@(private="package") @(private="package")
_process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) { _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
info.pid = pid info.pid = pid
defer if err != nil { // Note(flysand): Open the process handle right away to prevent some race
free_process_info(info, allocator) // conditions. Once the handle is open, the process will be kept alive by
} // the OS.
// 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}
}
ph := win32.INVALID_HANDLE_VALUE ph := win32.INVALID_HANDLE_VALUE
if selection >= {.Command_Line, .Environment, .Working_Dir, .Username} {
if selection >= {.Command_Line, .Environment, .Working_Dir, .Username} { // need process handle
ph = win32.OpenProcess( ph = win32.OpenProcess(
win32.PROCESS_QUERY_LIMITED_INFORMATION | win32.PROCESS_VM_READ, win32.PROCESS_QUERY_LIMITED_INFORMATION | win32.PROCESS_VM_READ,
false, false,
@@ -134,84 +111,15 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
defer if ph != win32.INVALID_HANDLE_VALUE { defer if ph != win32.INVALID_HANDLE_VALUE {
win32.CloseHandle(ph) win32.CloseHandle(ph)
} }
snapshot_process: if selection >= {.PPid, .Priority} {
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
entry, entry_err := _process_entry_by_pid(info.pid) entry, entry_err := _process_entry_by_pid(info.pid)
if entry_err != nil { if entry_err != nil {
err = General_Error.Not_Exist err = entry_err
if entry_err == General_Error.Not_Exist {
return return
} else {
break snapshot_process
}
} }
if .PPid in selection { if .PPid in selection {
info.fields += {.PPid} info.fields += {.PPid}
@@ -222,12 +130,18 @@ _process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields
info.priority = int(entry.pcPriClassBase) info.priority = int(entry.pcPriClassBase)
} }
} }
if .Executable_Path in selection { // snap module snapshot_modules: if .Executable_Path in selection {
info.executable_path = _process_exe_by_pid(pid, allocator) or_return 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} info.fields += {.Executable_Path}
} }
ph := win32.HANDLE(process.handle) read_peb: if selection >= {.Command_Line, .Environment, .Working_Dir} {
if selection >= {.Command_Line, .Environment, .Working_Dir} { // need peb
process_info_size: u32 process_info_size: u32
process_info: win32.PROCESS_BASIC_INFORMATION process_info: win32.PROCESS_BASIC_INFORMATION
status := win32.NtQueryInformationProcess(ph, .ProcessBasicInformation, &process_info, size_of(process_info), &process_info_size) 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 // TODO(flysand): There's probably a mismatch between NTSTATUS and
// windows userland error codes, I haven't checked. // windows userland error codes, I haven't checked.
err = Platform_Error(status) err = Platform_Error(status)
return break read_peb
} }
if process_info.PebBaseAddress == nil { assert(process_info.PebBaseAddress != nil)
// Not sure what the error is
err = General_Error.Unsupported
return
}
process_peb: win32.PEB 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 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} { if selection >= {.Command_Line, .Command_Args} {
TEMP_ALLOCATOR_GUARD() TEMP_ALLOCATOR_GUARD()
cmdline_w := make([]u16, process_params.CommandLine.Length, temp_allocator()) or_return 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 { if .Command_Line in selection {
info.command_line = win32_utf16_to_utf8(cmdline_w, allocator) or_return info.command_line = win32_utf16_to_utf8(cmdline_w, allocator) or_return
info.fields += {.Command_Line} info.fields += {.Command_Line}
@@ -263,28 +178,147 @@ _process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields
info.fields += {.Command_Args} info.fields += {.Command_Args}
} }
} }
if .Environment in selection { if .Environment in selection {
TEMP_ALLOCATOR_GUARD() TEMP_ALLOCATOR_GUARD()
env_len := process_params.EnvironmentSize / 2 env_len := process_params.EnvironmentSize / 2
envs_w := make([]u16, env_len, temp_allocator()) or_return 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.environment = _parse_environment_block(raw_data(envs_w), allocator) or_return
info.fields += {.Environment} info.fields += {.Environment}
} }
if .Working_Dir in selection { if .Working_Dir in selection {
TEMP_ALLOCATOR_GUARD() TEMP_ALLOCATOR_GUARD()
cwd_w := make([]u16, process_params.CurrentDirectoryPath.Length, temp_allocator()) or_return 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.working_dir = win32_utf16_to_utf8(cwd_w, allocator) or_return
info.fields += {.Working_Dir} info.fields += {.Working_Dir}
} }
} }
if .Username in selection { read_username: if .Username in selection {
info.username = _get_process_user(ph, allocator) or_return 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} info.fields += {.Username}
} }
err = nil err = nil
@@ -294,15 +328,15 @@ _process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields
@(private="package") @(private="package")
_current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) { _current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
info.pid = get_pid() info.pid = get_pid()
defer if err != nil { snapshot_process: if selection >= {.PPid, .Priority} {
free_process_info(info, allocator)
}
if selection >= {.PPid, .Priority} { // snap process
entry, entry_err := _process_entry_by_pid(info.pid) entry, entry_err := _process_entry_by_pid(info.pid)
if entry_err != nil { if entry_err != nil {
err = General_Error.Not_Exist err = entry_err
if entry_err == General_Error.Not_Exist {
return return
} else {
break snapshot_process
}
} }
if .PPid in selection { if .PPid in selection {
info.fields += {.PPid} info.fields += {.PPid}
@@ -313,14 +347,16 @@ _current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime
info.priority = int(entry.pcPriClassBase) info.priority = int(entry.pcPriClassBase)
} }
} }
if .Executable_Path in selection { module_filename: if .Executable_Path in selection {
exe_filename_w: [256]u16 exe_filename_w: [256]u16
path_len := win32.GetModuleFileNameW(nil, raw_data(exe_filename_w[:]), len(exe_filename_w)) 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.executable_path = win32_utf16_to_utf8(exe_filename_w[:path_len], allocator) or_return
info.fields += {.Executable_Path} info.fields += {.Executable_Path}
} }
if selection >= {.Command_Line, .Command_Args} { command_line: if selection >= {.Command_Line, .Command_Args} {
command_line_w := win32.GetCommandLineW() command_line_w := win32.GetCommandLineW()
assert(command_line_w != nil)
if .Command_Line in selection { if .Command_Line in selection {
info.command_line = win32_wstring_to_utf8(command_line_w, allocator) or_return info.command_line = win32_wstring_to_utf8(command_line_w, allocator) or_return
info.fields += {.Command_Line} info.fields += {.Command_Line}
@@ -330,14 +366,22 @@ _current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime
info.fields += {.Command_Args} info.fields += {.Command_Args}
} }
} }
if .Environment in selection { read_environment: if .Environment in selection {
env_block := win32.GetEnvironmentStringsW() env_block := win32.GetEnvironmentStringsW()
assert(env_block != nil)
info.environment = _parse_environment_block(env_block, allocator) or_return info.environment = _parse_environment_block(env_block, allocator) or_return
info.fields += {.Environment} info.fields += {.Environment}
} }
if .Username in selection { read_username: if .Username in selection {
process_handle := win32.GetCurrentProcess() 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} info.fields += {.Username}
} }
if .Working_Dir in selection { if .Working_Dir in selection {
+5
View File
@@ -19,6 +19,7 @@ iterate_array :: proc(val: any, it: ^int) -> (elem: any, index: int, ok: bool) {
elem.data = rawptr(uintptr(val.data) + uintptr(it^ * info.elem_size)) elem.data = rawptr(uintptr(val.data) + uintptr(it^ * info.elem_size))
elem.id = info.elem.id elem.id = info.elem.id
ok = true ok = true
index = it^
it^ += 1 it^ += 1
} }
case Type_Info_Slice: case Type_Info_Slice:
@@ -27,6 +28,7 @@ iterate_array :: proc(val: any, it: ^int) -> (elem: any, index: int, ok: bool) {
elem.data = rawptr(uintptr(array.data) + uintptr(it^ * info.elem_size)) elem.data = rawptr(uintptr(array.data) + uintptr(it^ * info.elem_size))
elem.id = info.elem.id elem.id = info.elem.id
ok = true ok = true
index = it^
it^ += 1 it^ += 1
} }
case Type_Info_Dynamic_Array: case Type_Info_Dynamic_Array:
@@ -35,6 +37,7 @@ iterate_array :: proc(val: any, it: ^int) -> (elem: any, index: int, ok: bool) {
elem.data = rawptr(uintptr(array.data) + uintptr(it^ * info.elem_size)) elem.data = rawptr(uintptr(array.data) + uintptr(it^ * info.elem_size))
elem.id = info.elem.id elem.id = info.elem.id
ok = true ok = true
index = it^
it^ += 1 it^ += 1
} }
} }
@@ -69,6 +72,7 @@ iterate_map :: proc(val: any, it: ^int) -> (key, value: any, ok: bool) {
key.id = info.key.id key.id = info.key.id
value.id = info.value.id value.id = info.value.id
ok = true ok = true
it^ += 1
break break
} }
@@ -76,3 +80,4 @@ iterate_map :: proc(val: any, it: ^int) -> (key, value: any, ok: bool) {
} }
return return
} }
+6 -13
View File
@@ -181,23 +181,16 @@ foreign CoreFoundation {
STR :: StringMakeConstantString STR :: StringMakeConstantString
StringCopyToOdinString :: proc( StringCopyToOdinString :: proc(theString: String, allocator := context.allocator) -> (str: string, ok: bool) #optional_ok {
theString: String,
allocator := context.allocator,
) -> (
str: string,
ok: bool,
) #optional_ok {
length := StringGetLength(theString) length := StringGetLength(theString)
max := StringGetMaximumSizeForEncoding(length, StringEncoding(StringBuiltInEncodings.UTF8)) max := StringGetMaximumSizeForEncoding(length, StringEncoding(StringBuiltInEncodings.UTF8))
buf, err := make([]byte, max, allocator) buf, err := make([]byte, max, allocator)
if err != nil { return } if err != nil {
return
raw_str := runtime.Raw_String {
data = raw_data(buf),
} }
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
} }
+1 -1
View File
@@ -4,7 +4,7 @@ package sys_windows
foreign import dwmapi "system:Dwmapi.lib" foreign import dwmapi "system:Dwmapi.lib"
DWMWINDOWATTRIBUTE :: enum { DWMWINDOWATTRIBUTE :: enum {
DWMWA_NCRENDERING_ENABLED, DWMWA_NCRENDERING_ENABLED = 1,
DWMWA_NCRENDERING_POLICY, DWMWA_NCRENDERING_POLICY,
DWMWA_TRANSITIONS_FORCEDISABLED, DWMWA_TRANSITIONS_FORCEDISABLED,
DWMWA_ALLOW_NCPAINT, DWMWA_ALLOW_NCPAINT,
+6 -2
View File
@@ -175,10 +175,12 @@ pool_stop_task :: proc(pool: ^Pool, user_index: int, exit_code: int = 1) -> bool
intrinsics.atomic_sub(&pool.num_outstanding, 1) intrinsics.atomic_sub(&pool.num_outstanding, 1)
intrinsics.atomic_sub(&pool.num_in_processing, 1) intrinsics.atomic_sub(&pool.num_in_processing, 1)
old_thread_user_index := t.user_index
destroy(t) destroy(t)
replacement := create(pool_thread_runner) replacement := create(pool_thread_runner)
replacement.user_index = t.user_index replacement.user_index = old_thread_user_index
replacement.data = data replacement.data = data
data.task = {} data.task = {}
pool.threads[i] = replacement pool.threads[i] = replacement
@@ -207,10 +209,12 @@ pool_stop_all_tasks :: proc(pool: ^Pool, exit_code: int = 1) {
intrinsics.atomic_sub(&pool.num_outstanding, 1) intrinsics.atomic_sub(&pool.num_outstanding, 1)
intrinsics.atomic_sub(&pool.num_in_processing, 1) intrinsics.atomic_sub(&pool.num_in_processing, 1)
old_thread_user_index := t.user_index
destroy(t) destroy(t)
replacement := create(pool_thread_runner) replacement := create(pool_thread_runner)
replacement.user_index = t.user_index replacement.user_index = old_thread_user_index
replacement.data = data replacement.data = data
data.task = {} data.task = {}
pool.threads[i] = replacement pool.threads[i] = replacement
+5 -1
View File
@@ -2,6 +2,7 @@
// +private // +private
package thread package thread
import "base:runtime"
import "core:sync" import "core:sync"
import "core:sys/unix" import "core:sys/unix"
import "core:time" 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 // 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! // variable above. We must perform that waiting BEFORE we select the context!
context = _select_context_for_thread(init_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) t.procedure(t)
} }
+5 -1
View File
@@ -3,6 +3,7 @@
package thread package thread
import "base:intrinsics" import "base:intrinsics"
import "base:runtime"
import "core:sync" import "core:sync"
import win32 "core:sys/windows" 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 // 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(). // to t.procedure().
context = _select_context_for_thread(init_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) t.procedure(t)
} }
+61
View File
@@ -2048,6 +2048,14 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
return ok; return ok;
} }
if (BuiltinProc__atomic_begin < id && id < BuiltinProc__atomic_end) {
if (build_context.metrics.arch == TargetArch_riscv64) {
if (!check_target_feature_is_enabled(str_lit("a"), nullptr)) {
error(call, "missing required target feature \"a\" for atomics, enable it by setting a different -microarch or explicitly adding it through -target-features");
}
}
}
switch (id) { switch (id) {
default: default:
GB_PANIC("Implement built-in procedure: %.*s", LIT(builtin_name)); GB_PANIC("Implement built-in procedure: %.*s", LIT(builtin_name));
@@ -5665,6 +5673,59 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
break; break;
} }
break; break;
case BuiltinProc_type_has_shared_fields:
{
Type *u = check_type(c, ce->args[0]);
Type *ut = base_type(u);
if (ut == nullptr || ut == t_invalid) {
error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name));
return false;
}
if (ut->kind != Type_Struct || ut->Struct.soa_kind != StructSoa_None) {
gbString t = type_to_string(ut);
error(ce->args[0], "Expected a struct type for '%.*s', got %s", LIT(builtin_name), t);
gb_string_free(t);
return false;
}
Type *v = check_type(c, ce->args[1]);
Type *vt = base_type(v);
if (vt == nullptr || vt == t_invalid) {
error(ce->args[1], "Expected a type for '%.*s'", LIT(builtin_name));
return false;
}
if (vt->kind != Type_Struct || vt->Struct.soa_kind != StructSoa_None) {
gbString t = type_to_string(vt);
error(ce->args[1], "Expected a struct type for '%.*s', got %s", LIT(builtin_name), t);
gb_string_free(t);
return false;
}
bool is_shared = true;
for (Entity *v_field : vt->Struct.fields) {
bool found = false;
for (Entity *u_field : ut->Struct.fields) {
if (v_field->token.string == u_field->token.string &&
are_types_identical(v_field->type, u_field->type)) {
found = true;
break;
}
}
if (!found) {
is_shared = false;
break;
}
}
operand->mode = Addressing_Constant;
operand->value = exact_value_bool(is_shared);
operand->type = t_untyped_bool;
break;
}
case BuiltinProc_type_field_type: case BuiltinProc_type_field_type:
{ {
Operand op = {}; Operand op = {};
+30 -7
View File
@@ -1180,11 +1180,15 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ
LIT(article), LIT(article),
LIT(context_name)); LIT(context_name));
} else { } else {
ERROR_BLOCK();
error(operand->expr, error(operand->expr,
"Cannot assign '%s', a type, to %.*s%.*s", "Cannot assign '%s', a type, to %.*s%.*s",
op_type_str, op_type_str,
LIT(article), LIT(article),
LIT(context_name)); LIT(context_name));
if (type && are_types_identical(type, t_any)) {
error_line("\tSuggestion: 'typeid_of(%s)'", expr_str);
}
} }
break; break;
default: default:
@@ -3782,10 +3786,10 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ
// NOTE(bill): Allow comparisons between types // NOTE(bill): Allow comparisons between types
if (is_ise_expr(be->left)) { if (is_ise_expr(be->left)) {
// Evalute the right before the left for an '.X' expression // Evalute the right before the left for an '.X' expression
check_expr_or_type(c, y, be->right, type_hint); check_expr_or_type(c, y, be->right, nullptr /* ignore type hint */);
check_expr_or_type(c, x, be->left, y->type); check_expr_or_type(c, x, be->left, y->type);
} else { } else {
check_expr_or_type(c, x, be->left, type_hint); check_expr_or_type(c, x, be->left, nullptr /* ignore type hint */);
check_expr_or_type(c, y, be->right, x->type); check_expr_or_type(c, y, be->right, x->type);
} }
bool xt = x->mode == Addressing_Type; bool xt = x->mode == Addressing_Type;
@@ -4658,7 +4662,8 @@ gb_internal bool check_index_value(CheckerContext *c, Type *main_type, bool open
check_expr_with_type_hint(c, &operand, index_value, type_hint); check_expr_with_type_hint(c, &operand, index_value, type_hint);
if (operand.mode == Addressing_Invalid) { if (operand.mode == Addressing_Invalid) {
if (value) *value = 0; if (value) *value = 0;
return false; // NOTE(bill): return true here to propagate the errors better
return true;
} }
Type *index_type = t_int; Type *index_type = t_int;
@@ -4879,7 +4884,7 @@ gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue v
TypeAndValue tav = fv->value->tav; TypeAndValue tav = fv->value->tav;
if (success_) *success_ = true; if (success_) *success_ = true;
if (finish_) *finish_ = false; if (finish_) *finish_ = false;
return tav.value;; return tav.value;
} }
} }
@@ -4954,7 +4959,6 @@ gb_internal ExactValue get_constant_field(CheckerContext *c, Operand const *oper
return value; return value;
} }
} }
if (success_) *success_ = true; if (success_) *success_ = true;
return value; return value;
} else if (value.kind == ExactValue_Quaternion) { } else if (value.kind == ExactValue_Quaternion) {
@@ -7625,7 +7629,7 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
gbString s = gb_string_make_reserve(heap_allocator(), e->token.string.len+3); gbString s = gb_string_make_reserve(heap_allocator(), e->token.string.len+3);
s = gb_string_append_fmt(s, "%.*s(", LIT(e->token.string)); s = gb_string_append_fmt(s, "%.*s(", LIT(e->token.string));
TypeTuple *tuple = get_record_polymorphic_params(e->type); TypeTuple *tuple = get_record_polymorphic_params(bt);
if (tuple != nullptr) for_array(i, tuple->variables) { if (tuple != nullptr) for_array(i, tuple->variables) {
Entity *v = tuple->variables[i]; Entity *v = tuple->variables[i];
String name = v->token.string; String name = v->token.string;
@@ -7640,10 +7644,12 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
s = write_type_to_string(s, v->type, false); s = write_type_to_string(s, v->type, false);
} }
} else if (v->kind == Entity_Constant) { } else if (v->kind == Entity_Constant) {
if (v->Constant.value.kind != ExactValue_Invalid) {
s = gb_string_append_fmt(s, "="); s = gb_string_append_fmt(s, "=");
s = write_exact_value_to_string(s, v->Constant.value); s = write_exact_value_to_string(s, v->Constant.value);
} }
} }
}
s = gb_string_append_fmt(s, ")"); s = gb_string_append_fmt(s, ")");
String new_name = make_string_c(s); String new_name = make_string_c(s);
@@ -10055,6 +10061,22 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
is_constant = o->mode == Addressing_Constant; is_constant = o->mode == Addressing_Constant;
} }
if (elem->kind == Ast_BinaryExpr) {
switch (elem->BinaryExpr.op.kind) {
case Token_Or:
{
gbString x = expr_to_string(elem->BinaryExpr.left);
gbString y = expr_to_string(elem->BinaryExpr.right);
gbString e = expr_to_string(elem);
error(elem, "Was the following intended? '%s, %s'; if not, surround the expression with parentheses '(%s)'", x, y, e);
gb_string_free(e);
gb_string_free(y);
gb_string_free(x);
}
break;
}
}
check_assignment(c, o, t->BitSet.elem, str_lit("bit_set literal")); check_assignment(c, o, t->BitSet.elem, str_lit("bit_set literal"));
if (o->mode == Addressing_Constant) { if (o->mode == Addressing_Constant) {
i64 lower = t->BitSet.lower; i64 lower = t->BitSet.lower;
@@ -10544,7 +10566,8 @@ gb_internal ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node,
o->expr = node; o->expr = node;
return kind; return kind;
} else if (ok && !is_type_matrix(t)) { } else if (ok && !is_type_matrix(t)) {
ExactValue value = type_and_value_of_expr(ie->expr).value; TypeAndValue tav = type_and_value_of_expr(ie->expr);
ExactValue value = tav.value;
o->mode = Addressing_Constant; o->mode = Addressing_Constant;
bool success = false; bool success = false;
bool finish = false; bool finish = false;
+10 -1
View File
@@ -199,6 +199,9 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit)
} }
break; break;
case Ast_DeferStmt:
return check_has_break(stmt->DeferStmt.stmt, label, implicit);
case Ast_BlockStmt: case Ast_BlockStmt:
return check_has_break_list(stmt->BlockStmt.stmts, label, implicit); return check_has_break_list(stmt->BlockStmt.stmts, label, implicit);
@@ -1695,7 +1698,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
} }
} }
} }
bool is_ptr = type_deref(operand.type); bool is_ptr = is_type_pointer(type_deref(operand.type));
Type *t = base_type(type_deref(operand.type)); Type *t = base_type(type_deref(operand.type));
switch (t->kind) { switch (t->kind) {
@@ -1735,6 +1738,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
break; break;
case Type_EnumeratedArray: case Type_EnumeratedArray:
is_possibly_addressable = operand.mode == Addressing_Variable || is_ptr;
array_add(&vals, t->EnumeratedArray.elem); array_add(&vals, t->EnumeratedArray.elem);
array_add(&vals, t->EnumeratedArray.index); array_add(&vals, t->EnumeratedArray.index);
break; break;
@@ -2706,6 +2710,7 @@ gb_internal void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags)
error(bs->label, "A branch statement's label name must be an identifier"); error(bs->label, "A branch statement's label name must be an identifier");
return; return;
} }
Ast *ident = bs->label; Ast *ident = bs->label;
String name = ident->Ident.token.string; String name = ident->Ident.token.string;
Operand o = {}; Operand o = {};
@@ -2737,6 +2742,10 @@ gb_internal void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags)
break; break;
} }
if (ctx->in_defer) {
error(bs->label, "A labelled '%.*s' cannot be used within a 'defer'", LIT(token.string));
}
} }
case_end; case_end;
+29 -9
View File
@@ -3174,8 +3174,8 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_block_decl_attribute) {
return true; return true;
} else if (name == "link_prefix") { } else if (name == "link_prefix") {
if (ev.kind == ExactValue_String) { if (ev.kind == ExactValue_String) {
String link_prefix = ev.value_string; String link_prefix = string_trim_whitespace(ev.value_string);
if (!is_foreign_name_valid(link_prefix)) { if (link_prefix.len != 0 && !is_foreign_name_valid(link_prefix)) {
error(elem, "Invalid link prefix: '%.*s'", LIT(link_prefix)); error(elem, "Invalid link prefix: '%.*s'", LIT(link_prefix));
} else { } else {
c->foreign_context.link_prefix = link_prefix; c->foreign_context.link_prefix = link_prefix;
@@ -3186,8 +3186,8 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_block_decl_attribute) {
return true; return true;
} else if (name == "link_suffix") { } else if (name == "link_suffix") {
if (ev.kind == ExactValue_String) { if (ev.kind == ExactValue_String) {
String link_suffix = ev.value_string; String link_suffix = string_trim_whitespace(ev.value_string);
if (!is_foreign_name_valid(link_suffix)) { if (link_suffix.len != 0 && !is_foreign_name_valid(link_suffix)) {
error(elem, "Invalid link suffix: '%.*s'", LIT(link_suffix)); error(elem, "Invalid link suffix: '%.*s'", LIT(link_suffix));
} else { } else {
c->foreign_context.link_suffix = link_suffix; c->foreign_context.link_suffix = link_suffix;
@@ -3489,7 +3489,7 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
if (ev.kind == ExactValue_String) { if (ev.kind == ExactValue_String) {
ac->link_prefix = ev.value_string; ac->link_prefix = ev.value_string;
if (!is_foreign_name_valid(ac->link_prefix)) { if (ac->link_prefix.len != 0 && !is_foreign_name_valid(ac->link_prefix)) {
error(elem, "Invalid link prefix: %.*s", LIT(ac->link_prefix)); error(elem, "Invalid link prefix: %.*s", LIT(ac->link_prefix));
} }
} else { } else {
@@ -3501,7 +3501,7 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
if (ev.kind == ExactValue_String) { if (ev.kind == ExactValue_String) {
ac->link_suffix = ev.value_string; ac->link_suffix = ev.value_string;
if (!is_foreign_name_valid(ac->link_suffix)) { if (ac->link_suffix.len != 0 && !is_foreign_name_valid(ac->link_suffix)) {
error(elem, "Invalid link suffix: %.*s", LIT(ac->link_suffix)); error(elem, "Invalid link suffix: %.*s", LIT(ac->link_suffix));
} }
} else { } else {
@@ -3774,7 +3774,7 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) {
ExactValue ev = check_decl_attribute_value(c, value); ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind == ExactValue_String) { if (ev.kind == ExactValue_String) {
ac->link_prefix = ev.value_string; ac->link_prefix = ev.value_string;
if (!is_foreign_name_valid(ac->link_prefix)) { if (ac->link_prefix.len != 0 && !is_foreign_name_valid(ac->link_prefix)) {
error(elem, "Invalid link prefix: %.*s", LIT(ac->link_prefix)); error(elem, "Invalid link prefix: %.*s", LIT(ac->link_prefix));
} }
} else { } else {
@@ -3785,7 +3785,7 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) {
ExactValue ev = check_decl_attribute_value(c, value); ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind == ExactValue_String) { if (ev.kind == ExactValue_String) {
ac->link_suffix = ev.value_string; ac->link_suffix = ev.value_string;
if (!is_foreign_name_valid(ac->link_suffix)) { if (ac->link_suffix.len != 0 && !is_foreign_name_valid(ac->link_suffix)) {
error(elem, "Invalid link suffix: %.*s", LIT(ac->link_suffix)); error(elem, "Invalid link suffix: %.*s", LIT(ac->link_suffix));
} }
} else { } else {
@@ -4532,7 +4532,9 @@ gb_internal void check_collect_entities(CheckerContext *c, Slice<Ast *> const &n
case_end; case_end;
case_ast_node(fb, ForeignBlockDecl, decl); case_ast_node(fb, ForeignBlockDecl, decl);
check_add_foreign_block_decl(c, decl); if (curr_file != nullptr) {
array_add(&curr_file->delayed_decls_queues[AstDelayQueue_ForeignBlock], decl);
}
case_end; case_end;
default: default:
@@ -4548,6 +4550,14 @@ gb_internal void check_collect_entities(CheckerContext *c, Slice<Ast *> const &n
// NOTE(bill): 'when' stmts need to be handled after the other as the condition may refer to something // NOTE(bill): 'when' stmts need to be handled after the other as the condition may refer to something
// declared after this stmt in source // declared after this stmt in source
if (curr_file == nullptr) { if (curr_file == nullptr) {
// For 'foreign' block statements that are not in file scope.
for_array(decl_index, nodes) {
Ast *decl = nodes[decl_index];
if (decl->kind == Ast_ForeignBlockDecl) {
check_add_foreign_block_decl(c, decl);
}
}
for_array(decl_index, nodes) { for_array(decl_index, nodes) {
Ast *decl = nodes[decl_index]; Ast *decl = nodes[decl_index];
if (decl->kind == Ast_WhenStmt) { if (decl->kind == Ast_WhenStmt) {
@@ -5508,6 +5518,16 @@ gb_internal void check_import_entities(Checker *c) {
correct_type_aliases_in_scope(&ctx, pkg->scope); correct_type_aliases_in_scope(&ctx, pkg->scope);
} }
for_array(i, pkg->files) {
AstFile *f = pkg->files[i];
reset_checker_context(&ctx, f, &untyped);
for (Ast *decl : f->delayed_decls_queues[AstDelayQueue_ForeignBlock]) {
check_add_foreign_block_decl(&ctx, decl);
}
array_clear(&f->delayed_decls_queues[AstDelayQueue_ForeignBlock]);
}
for_array(i, pkg->files) { for_array(i, pkg->files) {
AstFile *f = pkg->files[i]; AstFile *f = pkg->files[i];
reset_checker_context(&ctx, f, &untyped); reset_checker_context(&ctx, f, &untyped);
+7
View File
@@ -99,6 +99,7 @@ enum BuiltinProcId {
BuiltinProc_prefetch_write_instruction, BuiltinProc_prefetch_write_instruction,
BuiltinProc_prefetch_write_data, BuiltinProc_prefetch_write_data,
BuiltinProc__atomic_begin,
BuiltinProc_atomic_type_is_lock_free, BuiltinProc_atomic_type_is_lock_free,
BuiltinProc_atomic_thread_fence, BuiltinProc_atomic_thread_fence,
BuiltinProc_atomic_signal_fence, BuiltinProc_atomic_signal_fence,
@@ -124,6 +125,7 @@ enum BuiltinProcId {
BuiltinProc_atomic_compare_exchange_strong_explicit, BuiltinProc_atomic_compare_exchange_strong_explicit,
BuiltinProc_atomic_compare_exchange_weak, BuiltinProc_atomic_compare_exchange_weak,
BuiltinProc_atomic_compare_exchange_weak_explicit, BuiltinProc_atomic_compare_exchange_weak_explicit,
BuiltinProc__atomic_end,
BuiltinProc_fixed_point_mul, BuiltinProc_fixed_point_mul,
BuiltinProc_fixed_point_div, BuiltinProc_fixed_point_div,
@@ -313,6 +315,8 @@ BuiltinProc__type_simple_boolean_end,
BuiltinProc_type_map_info, BuiltinProc_type_map_info,
BuiltinProc_type_map_cell_info, BuiltinProc_type_map_cell_info,
BuiltinProc_type_has_shared_fields,
BuiltinProc__type_end, BuiltinProc__type_end,
BuiltinProc_procedure_of, BuiltinProc_procedure_of,
@@ -436,6 +440,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("prefetch_write_instruction"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("prefetch_write_instruction"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("prefetch_write_data"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("prefetch_write_data"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_type_is_lock_free"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_type_is_lock_free"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_thread_fence"), 1, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_thread_fence"), 1, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_signal_fence"), 1, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_signal_fence"), 1, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
@@ -461,6 +466,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("atomic_compare_exchange_strong_explicit"), 5, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, {STR_LIT("atomic_compare_exchange_strong_explicit"), 5, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_compare_exchange_weak"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, {STR_LIT("atomic_compare_exchange_weak"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_compare_exchange_weak_explicit"), 5, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, {STR_LIT("atomic_compare_exchange_weak_explicit"), 5, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("fixed_point_mul"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("fixed_point_mul"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("fixed_point_div"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("fixed_point_div"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -647,6 +653,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_map_info"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_map_info"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_map_cell_info"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_map_cell_info"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_has_shared_fields"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+13 -3
View File
@@ -55,6 +55,16 @@ gb_internal void lb_debug_file_line(lbModule *m, Ast *node, LLVMMetadataRef *fil
} }
} }
gb_internal LLVMMetadataRef lb_debug_procedure_parameters(lbModule *m, Type *type) {
if (is_type_proc(type)) {
return lb_debug_type(m, t_rawptr);
}
if (type->kind == Type_Tuple && type->Tuple.variables.count == 1) {
return lb_debug_procedure_parameters(m, type->Tuple.variables[0]->type);
}
return lb_debug_type(m, type);
}
gb_internal LLVMMetadataRef lb_debug_type_internal_proc(lbModule *m, Type *type) { gb_internal LLVMMetadataRef lb_debug_type_internal_proc(lbModule *m, Type *type) {
i64 size = type_size_of(type); // Check size i64 size = type_size_of(type); // Check size
gb_unused(size); gb_unused(size);
@@ -78,7 +88,7 @@ gb_internal LLVMMetadataRef lb_debug_type_internal_proc(lbModule *m, Type *type)
if (type->Proc.result_count == 0) { if (type->Proc.result_count == 0) {
parameters[param_index++] = nullptr; parameters[param_index++] = nullptr;
} else { } else {
parameters[param_index++] = lb_debug_type(m, type->Proc.results); parameters[param_index++] = lb_debug_procedure_parameters(m, type->Proc.results);
} }
LLVMMetadataRef file = nullptr; LLVMMetadataRef file = nullptr;
@@ -88,7 +98,7 @@ gb_internal LLVMMetadataRef lb_debug_type_internal_proc(lbModule *m, Type *type)
if (e->kind != Entity_Variable) { if (e->kind != Entity_Variable) {
continue; continue;
} }
parameters[param_index] = lb_debug_type(m, e->type); parameters[param_index] = lb_debug_procedure_parameters(m, e->type);
param_index += 1; param_index += 1;
} }
@@ -969,7 +979,7 @@ gb_internal LLVMMetadataRef lb_debug_type(lbModule *m, Type *type) {
return lb_debug_struct(m, type, bt, name, scope, file, line); return lb_debug_struct(m, type, bt, name, scope, file, line);
} }
case Type_Struct: return lb_debug_struct(m, type, base_type(type), name, scope, file, line); case Type_Struct: return lb_debug_struct(m, type, bt, name, scope, file, line);
case Type_Slice: return lb_debug_slice(m, type, name, scope, file, line); case Type_Slice: return lb_debug_slice(m, type, name, scope, file, line);
case Type_DynamicArray: return lb_debug_dynamic_array(m, type, name, scope, file, line); case Type_DynamicArray: return lb_debug_dynamic_array(m, type, name, scope, file, line);
case Type_Union: return lb_debug_union(m, type, name, scope, file, line); case Type_Union: return lb_debug_union(m, type, name, scope, file, line);
+1 -1
View File
@@ -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)) { if (is_type_simd_vector(src) && is_type_simd_vector(dst)) {
res.value = LLVMBuildBitCast(p->builder, value.value, lb_type(p->module, t), ""); res.value = LLVMBuildBitCast(p->builder, value.value, lb_type(p->module, t), "");
return res; 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)); 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); lbValue ptr = lb_address_from_load_or_generate_local(p, value);
if (lb_try_update_alignment(ptr, align)) { if (lb_try_update_alignment(ptr, align)) {
+38 -8
View File
@@ -1921,6 +1921,9 @@ gb_internal Array<Ast *> parse_enum_field_list(AstFile *f) {
f->curr_token.kind != Token_EOF) { f->curr_token.kind != Token_EOF) {
CommentGroup *docs = f->lead_comment; CommentGroup *docs = f->lead_comment;
CommentGroup *comment = nullptr; CommentGroup *comment = nullptr;
parse_enforce_tabs(f);
Ast *name = parse_value(f); Ast *name = parse_value(f);
Ast *value = nullptr; Ast *value = nullptr;
if (f->curr_token.kind == Token_Eq) { if (f->curr_token.kind == Token_Eq) {
@@ -2259,6 +2262,7 @@ gb_internal Array<Ast *> parse_union_variant_list(AstFile *f) {
auto variants = array_make<Ast *>(ast_allocator(f)); auto variants = array_make<Ast *>(ast_allocator(f));
while (f->curr_token.kind != Token_CloseBrace && while (f->curr_token.kind != Token_CloseBrace &&
f->curr_token.kind != Token_EOF) { f->curr_token.kind != Token_EOF) {
parse_enforce_tabs(f);
Ast *type = parse_type(f); Ast *type = parse_type(f);
if (type->kind != Ast_BadExpr) { if (type->kind != Ast_BadExpr) {
array_add(&variants, type); array_add(&variants, type);
@@ -4274,6 +4278,7 @@ gb_internal Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_fl
while (f->curr_token.kind != follow && while (f->curr_token.kind != follow &&
f->curr_token.kind != Token_Colon && f->curr_token.kind != Token_Colon &&
f->curr_token.kind != Token_EOF) { f->curr_token.kind != Token_EOF) {
if (!is_signature) parse_enforce_tabs(f);
u32 flags = parse_field_prefixes(f); u32 flags = parse_field_prefixes(f);
Ast *param = parse_var_type(f, allow_ellipsis, allow_typeid_token); Ast *param = parse_var_type(f, allow_ellipsis, allow_typeid_token);
if (param->kind == Ast_Ellipsis) { if (param->kind == Ast_Ellipsis) {
@@ -4363,6 +4368,8 @@ gb_internal Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_fl
f->curr_token.kind != Token_EOF && f->curr_token.kind != Token_EOF &&
f->curr_token.kind != Token_Semicolon) { f->curr_token.kind != Token_Semicolon) {
CommentGroup *docs = f->lead_comment; CommentGroup *docs = f->lead_comment;
if (!is_signature) parse_enforce_tabs(f);
u32 set_flags = parse_field_prefixes(f); u32 set_flags = parse_field_prefixes(f);
Token tag = {}; Token tag = {};
Array<Ast *> names = parse_ident_list(f, allow_poly_names); Array<Ast *> names = parse_ident_list(f, allow_poly_names);
@@ -4532,6 +4539,10 @@ gb_internal Ast *parse_if_stmt(AstFile *f) {
return ast_bad_stmt(f, f->curr_token, f->curr_token); return ast_bad_stmt(f, f->curr_token, f->curr_token);
} }
Ast *top_if_stmt = nullptr;
Ast *prev_if_stmt = nullptr;
if_else_chain:;
Token token = expect_token(f, Token_if); Token token = expect_token(f, Token_if);
Ast *init = nullptr; Ast *init = nullptr;
Ast *cond = nullptr; Ast *cond = nullptr;
@@ -4573,12 +4584,24 @@ gb_internal Ast *parse_if_stmt(AstFile *f) {
ignore_strict_style = true; ignore_strict_style = true;
} }
skip_possible_newline_for_literal(f, ignore_strict_style); skip_possible_newline_for_literal(f, ignore_strict_style);
Ast *curr_if_stmt = ast_if_stmt(f, token, init, cond, body, nullptr);
if (top_if_stmt == nullptr) {
top_if_stmt = curr_if_stmt;
}
if (prev_if_stmt != nullptr) {
prev_if_stmt->IfStmt.else_stmt = curr_if_stmt;
}
if (f->curr_token.kind == Token_else) { if (f->curr_token.kind == Token_else) {
Token else_token = expect_token(f, Token_else); Token else_token = expect_token(f, Token_else);
switch (f->curr_token.kind) { switch (f->curr_token.kind) {
case Token_if: case Token_if:
else_stmt = parse_if_stmt(f); // NOTE(bill): Instead of relying on recursive descent for an if-else chain
break; // we can just inline the tail-recursion manually with a simple loop like
// construct using a `goto`
prev_if_stmt = curr_if_stmt;
goto if_else_chain;
case Token_OpenBrace: case Token_OpenBrace:
else_stmt = parse_block_stmt(f, false); else_stmt = parse_block_stmt(f, false);
break; break;
@@ -4593,7 +4616,9 @@ gb_internal Ast *parse_if_stmt(AstFile *f) {
} }
} }
return ast_if_stmt(f, token, init, cond, body, else_stmt); curr_if_stmt->IfStmt.else_stmt = else_stmt;
return top_if_stmt;
} }
gb_internal Ast *parse_when_stmt(AstFile *f) { gb_internal Ast *parse_when_stmt(AstFile *f) {
@@ -5357,6 +5382,11 @@ gb_internal u64 check_vet_flags(AstFile *file) {
gb_internal void parse_enforce_tabs(AstFile *f) { gb_internal void parse_enforce_tabs(AstFile *f) {
// Checks to see if tabs have been used for indentation
if ((check_vet_flags(f) & VetFlag_Tabs) == 0) {
return;
}
Token prev = f->prev_token; Token prev = f->prev_token;
Token curr = f->curr_token; Token curr = f->curr_token;
if (prev.pos.line < curr.pos.line) { if (prev.pos.line < curr.pos.line) {
@@ -5373,6 +5403,10 @@ gb_internal void parse_enforce_tabs(AstFile *f) {
isize len = end-it; isize len = end-it;
for (isize i = 0; i < len; i++) { for (isize i = 0; i < len; i++) {
if (it[i] == '/') {
// ignore comments
break;
}
if (it[i] == ' ') { if (it[i] == ' ') {
syntax_error(curr, "With '-vet-tabs', tabs must be used for indentation"); syntax_error(curr, "With '-vet-tabs', tabs must be used for indentation");
break; break;
@@ -5387,11 +5421,7 @@ gb_internal Array<Ast *> parse_stmt_list(AstFile *f) {
while (f->curr_token.kind != Token_case && while (f->curr_token.kind != Token_case &&
f->curr_token.kind != Token_CloseBrace && f->curr_token.kind != Token_CloseBrace &&
f->curr_token.kind != Token_EOF) { f->curr_token.kind != Token_EOF) {
// Checks to see if tabs have been used for indentation
if (check_vet_flags(f) & VetFlag_Tabs) {
parse_enforce_tabs(f); parse_enforce_tabs(f);
}
Ast *stmt = parse_stmt(f); Ast *stmt = parse_stmt(f);
if (stmt && stmt->kind != Ast_EmptyStmt) { if (stmt && stmt->kind != Ast_EmptyStmt) {
@@ -6416,7 +6446,7 @@ gb_internal bool parse_file(Parser *p, AstFile *f) {
} }
f->total_file_decl_count += calc_decl_count(stmt); f->total_file_decl_count += calc_decl_count(stmt);
if (stmt->kind == Ast_WhenStmt || stmt->kind == Ast_ExprStmt || stmt->kind == Ast_ImportDecl) { if (stmt->kind == Ast_WhenStmt || stmt->kind == Ast_ExprStmt || stmt->kind == Ast_ImportDecl || stmt->kind == Ast_ForeignBlockDecl) {
f->delayed_decl_count += 1; f->delayed_decl_count += 1;
} }
} }
+4
View File
@@ -82,6 +82,7 @@ enum AstFileFlag : u32 {
enum AstDelayQueueKind { enum AstDelayQueueKind {
AstDelayQueue_Import, AstDelayQueue_Import,
AstDelayQueue_Expr, AstDelayQueue_Expr,
AstDelayQueue_ForeignBlock,
AstDelayQueue_COUNT, AstDelayQueue_COUNT,
}; };
@@ -886,3 +887,6 @@ gb_internal Ast *alloc_ast_node(AstFile *f, AstKind kind);
gb_internal gbString expr_to_string(Ast *expression); gb_internal gbString expr_to_string(Ast *expression);
gb_internal bool allow_field_separator(AstFile *f); gb_internal bool allow_field_separator(AstFile *f);
gb_internal void parse_enforce_tabs(AstFile *f);
+1 -1
View File
@@ -429,7 +429,7 @@ map_with_integer_keys :: proc(t: ^testing.T) {
defer delete_map(my_map2) defer delete_map(my_map2)
unmarshal_err := json.unmarshal(marshaled_data, &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) runtime.delete_string(item)
} }
testing.expectf(t, unmarshal_err == nil, "Expected `json.unmarshal` to return nil, got %v", unmarshal_err) testing.expectf(t, unmarshal_err == nil, "Expected `json.unmarshal` to return nil, got %v", unmarshal_err)
@@ -11,6 +11,7 @@ import "core:testing"
test_temp_allocator_alignment_boundary :: proc(t: ^testing.T) { test_temp_allocator_alignment_boundary :: proc(t: ^testing.T) {
arena: runtime.Arena arena: runtime.Arena
context.allocator = runtime.arena_allocator(&arena) context.allocator = runtime.arena_allocator(&arena)
defer runtime.arena_destroy(&arena)
_, _ = mem.alloc(int(runtime.DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE)-120) _, _ = mem.alloc(int(runtime.DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE)-120)
_, err := mem.alloc(112, 32) _, err := mem.alloc(112, 32)
@@ -22,6 +23,7 @@ test_temp_allocator_alignment_boundary :: proc(t: ^testing.T) {
test_temp_allocator_big_alloc_and_alignment :: proc(t: ^testing.T) { test_temp_allocator_big_alloc_and_alignment :: proc(t: ^testing.T) {
arena: runtime.Arena arena: runtime.Arena
context.allocator = runtime.arena_allocator(&arena) context.allocator = runtime.arena_allocator(&arena)
defer runtime.arena_destroy(&arena)
mappy: map[[8]int]int mappy: map[[8]int]int
err := reserve(&mappy, 50000) err := reserve(&mappy, 50000)
@@ -32,6 +34,7 @@ test_temp_allocator_big_alloc_and_alignment :: proc(t: ^testing.T) {
test_temp_allocator_returns_correct_size :: proc(t: ^testing.T) { test_temp_allocator_returns_correct_size :: proc(t: ^testing.T) {
arena: runtime.Arena arena: runtime.Arena
context.allocator = runtime.arena_allocator(&arena) context.allocator = runtime.arena_allocator(&arena)
defer runtime.arena_destroy(&arena)
bytes, err := mem.alloc_bytes(10, 16) bytes, err := mem.alloc_bytes(10, 16)
testing.expect(t, err == nil) testing.expect(t, err == nil)
+1 -2
View File
@@ -16,8 +16,7 @@ run_test_cases :: proc(t: ^testing.T, test_cases: []Test_Case, loc := #caller_lo
result, _, _ := utf8.grapheme_count(c.str) result, _, _ := utf8.grapheme_count(c.str)
if !testing.expectf(t, result == c.expected_clusters, if !testing.expectf(t, result == c.expected_clusters,
"(#% 4i) graphemes: %i != %i, %q %s", i, result, c.expected_clusters, c.str, c.str, "(#% 4i) graphemes: %i != %i, %q %s", i, result, c.expected_clusters, c.str, c.str,
loc = loc) loc = loc) {
{
failed += 1 failed += 1
} }
} }
+1 -1
View File
@@ -338,7 +338,7 @@ foreign lib {
node_set_list_tight :: proc(node: ^Node, tight: b32) -> (success: b32) --- node_set_list_tight :: proc(node: ^Node, tight: b32) -> (success: b32) ---
// Returns the info string from a fenced code block. // Returns the info string from a fenced code block.
get_fence_info :: proc(node: ^Node) -> (fence_info: cstring) --- node_get_fence_info :: proc(node: ^Node) -> (fence_info: cstring) ---
// Sets the info string in a fenced code block, returning `true` on success and `false` on failure. // Sets the info string in a fenced code block, returning `true` on success and `false` on failure.
node_set_fence_info :: proc(node: ^Node, fence_info: cstring) -> (success: b32) --- node_set_fence_info :: proc(node: ^Node, fence_info: cstring) -> (success: b32) ---
+20 -5
View File
@@ -84,7 +84,6 @@ package raylib
import "core:c" import "core:c"
import "core:fmt" import "core:fmt"
import "core:mem" import "core:mem"
import "core:strings"
import "core:math/linalg" import "core:math/linalg"
_ :: linalg _ :: linalg
@@ -1029,7 +1028,6 @@ foreign lib {
SetTraceLogLevel :: proc(logLevel: TraceLogLevel) --- // Set the current threshold (minimum) log level SetTraceLogLevel :: proc(logLevel: TraceLogLevel) --- // Set the current threshold (minimum) log level
MemAlloc :: proc(size: c.uint) -> rawptr --- // Internal memory allocator MemAlloc :: proc(size: c.uint) -> rawptr --- // Internal memory allocator
MemRealloc :: proc(ptr: rawptr, size: c.uint) -> rawptr --- // Internal memory reallocator MemRealloc :: proc(ptr: rawptr, size: c.uint) -> rawptr --- // Internal memory reallocator
MemFree :: proc(ptr: rawptr) --- // Internal memory free
// Set custom callbacks // Set custom callbacks
// WARNING: Callbacks setup is intended for advance users // WARNING: Callbacks setup is intended for advance users
@@ -1256,7 +1254,7 @@ foreign lib {
LoadImage :: proc(fileName: cstring) -> Image --- // Load image from file into CPU memory (RAM) LoadImage :: proc(fileName: cstring) -> Image --- // Load image from file into CPU memory (RAM)
LoadImageRaw :: proc(fileName: cstring, width, height: c.int, format: PixelFormat, headerSize: c.int) -> Image --- // Load image from RAW file data LoadImageRaw :: proc(fileName: cstring, width, height: c.int, format: PixelFormat, headerSize: c.int) -> Image --- // Load image from RAW file data
LoadImageSvg :: proc(fileNameOrString: cstring, width, height: c.int) -> Image --- // Load image from SVG file data or string with specified size LoadImageSvg :: proc(fileNameOrString: cstring, width, height: c.int) -> Image --- // Load image from SVG file data or string with specified size
LoadImageAnim :: proc(fileName: cstring, frames: [^]c.int) -> Image --- // Load image sequence from file (frames appended to image.data) LoadImageAnim :: proc(fileName: cstring, frames: ^c.int) -> Image --- // Load image sequence from file (frames appended to image.data)
LoadImageFromMemory :: proc(fileType: cstring, fileData: rawptr, dataSize: c.int) -> Image --- // Load image from memory buffer, fileType refers to extension: i.e. '.png' LoadImageFromMemory :: proc(fileType: cstring, fileData: rawptr, dataSize: c.int) -> Image --- // Load image from memory buffer, fileType refers to extension: i.e. '.png'
LoadImageFromTexture :: proc(texture: Texture2D) -> Image --- // Load image from GPU texture data LoadImageFromTexture :: proc(texture: Texture2D) -> Image --- // Load image from GPU texture data
LoadImageFromScreen :: proc() -> Image --- // Load image from screen buffer and (screenshot) LoadImageFromScreen :: proc() -> Image --- // Load image from screen buffer and (screenshot)
@@ -1684,8 +1682,25 @@ TextFormat :: proc(text: cstring, args: ..any) -> cstring {
// Text formatting with variables (sprintf style) and allocates (must be freed with 'MemFree') // Text formatting with variables (sprintf style) and allocates (must be freed with 'MemFree')
TextFormatAlloc :: proc(text: cstring, args: ..any) -> cstring { TextFormatAlloc :: proc(text: cstring, args: ..any) -> cstring {
str := fmt.tprintf(string(text), ..args) return fmt.caprintf(string(text), ..args, allocator=MemAllocator())
return strings.clone_to_cstring(str, MemAllocator()) }
// Internal memory free
MemFree :: proc{
MemFreePtr,
MemFreeCstring,
}
@(default_calling_convention="c")
foreign lib {
@(link_name="MemFree")
MemFreePtr :: proc(ptr: rawptr) ---
}
MemFreeCstring :: proc "c" (s: cstring) {
MemFreePtr(rawptr(s))
} }
+6
View File
@@ -41,6 +41,12 @@ MAJOR_VERSION :: 2
MINOR_VERSION :: 0 MINOR_VERSION :: 0
PATCHLEVEL :: 16 PATCHLEVEL :: 16
VERSION :: proc "contextless" (ver: ^version) {
ver.major = MAJOR_VERSION
ver.minor = MINOR_VERSION
ver.patch = PATCHLEVEL
}
@(default_calling_convention="c", link_prefix="SDL_") @(default_calling_convention="c", link_prefix="SDL_")
foreign lib { foreign lib {
GetVersion :: proc(ver: ^version) --- GetVersion :: proc(ver: ^version) ---
+2 -2
View File
@@ -11,8 +11,8 @@ Have a look at the `example/` directory for the rendering of a basic triangle.
## Getting the wgpu-native libraries ## Getting the wgpu-native libraries
For native support (not the browser), some libraries are required. Fortunately this is 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), 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 v0.19.4.1 at the moment. 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). 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`), By default it will look for a static release version (`wgpu-OS-ARCH-release.a|lib`),
+3 -1
View File
@@ -158,15 +158,17 @@ frame :: proc "c" (dt: f32) {
view = frame, view = frame,
loadOp = .Clear, loadOp = .Clear,
storeOp = .Store, storeOp = .Store,
depthSlice = wgpu.DEPTH_SLICE_UNDEFINED,
clearValue = { 0, 1, 0, 1 }, clearValue = { 0, 1, 0, 1 },
}, },
}, },
) )
defer wgpu.RenderPassEncoderRelease(render_pass_encoder)
wgpu.RenderPassEncoderSetPipeline(render_pass_encoder, state.pipeline) wgpu.RenderPassEncoderSetPipeline(render_pass_encoder, state.pipeline)
wgpu.RenderPassEncoderDraw(render_pass_encoder, vertexCount=3, instanceCount=1, firstVertex=0, firstInstance=0) wgpu.RenderPassEncoderDraw(render_pass_encoder, vertexCount=3, instanceCount=1, firstVertex=0, firstInstance=0)
wgpu.RenderPassEncoderEnd(render_pass_encoder) wgpu.RenderPassEncoderEnd(render_pass_encoder)
wgpu.RenderPassEncoderRelease(render_pass_encoder)
command_buffer := wgpu.CommandEncoderFinish(command_encoder, nil) command_buffer := wgpu.CommandEncoderFinish(command_encoder, nil)
defer wgpu.CommandBufferRelease(command_buffer) defer wgpu.CommandBufferRelease(command_buffer)
+3 -1
View File
@@ -158,15 +158,17 @@ frame :: proc "c" (dt: f32) {
view = frame, view = frame,
loadOp = .Clear, loadOp = .Clear,
storeOp = .Store, storeOp = .Store,
depthSlice = wgpu.DEPTH_SLICE_UNDEFINED,
clearValue = { 0, 1, 0, 1 }, clearValue = { 0, 1, 0, 1 },
}, },
}, },
) )
defer wgpu.RenderPassEncoderRelease(render_pass_encoder)
wgpu.RenderPassEncoderSetPipeline(render_pass_encoder, state.pipeline) wgpu.RenderPassEncoderSetPipeline(render_pass_encoder, state.pipeline)
wgpu.RenderPassEncoderDraw(render_pass_encoder, vertexCount=3, instanceCount=1, firstVertex=0, firstInstance=0) wgpu.RenderPassEncoderDraw(render_pass_encoder, vertexCount=3, instanceCount=1, firstVertex=0, firstInstance=0)
wgpu.RenderPassEncoderEnd(render_pass_encoder) wgpu.RenderPassEncoderEnd(render_pass_encoder)
wgpu.RenderPassEncoderRelease(render_pass_encoder)
command_buffer := wgpu.CommandEncoderFinish(command_encoder, nil) command_buffer := wgpu.CommandEncoderFinish(command_encoder, nil)
defer wgpu.CommandBufferRelease(command_buffer) defer wgpu.CommandBufferRelease(command_buffer)
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+2
View File
@@ -5,7 +5,9 @@ import "vendor:wgpu"
GetSurface :: proc(instance: wgpu.Instance, window: ^sdl2.Window) -> wgpu.Surface { GetSurface :: proc(instance: wgpu.Instance, window: ^sdl2.Window) -> wgpu.Surface {
window_info: sdl2.SysWMinfo window_info: sdl2.SysWMinfo
sdl2.VERSION(&window_info.version)
sdl2.GetWindowWMInfo(window, &window_info) sdl2.GetWindowWMInfo(window, &window_info)
if window_info.subsystem == .WAYLAND { if window_info.subsystem == .WAYLAND {
display := window_info.info.wl.display display := window_info.info.wl.display
surface := window_info.info.wl.surface surface := window_info.info.wl.surface
+91 -96
View File
@@ -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", ], 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", ], PresentMode: ["fifo", "fifo-relaxed", "immediate", "mailbox", ],
TextureAspect: ["all", "stencil-only", "depth-only"], TextureAspect: ["all", "stencil-only", "depth-only"],
DeviceLostReason: [undefined, "unknown", "destroyed"],
}; };
/** @type {WebGPUObjectManager<{}>} */ /** @type {WebGPUObjectManager<{}>} */
@@ -382,13 +383,19 @@ class WebGPUInterface {
*/ */
RenderPassColorAttachment(start) { RenderPassColorAttachment(start) {
const viewIdx = this.mem.loadPtr(start + 4); 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 { return {
view: viewIdx > 0 ? this.textureViews.get(viewIdx) : undefined, view: viewIdx > 0 ? this.textureViews.get(viewIdx) : undefined,
resolveTarget: resolveTargetIdx > 0 ? this.textureViews.get(resolveTargetIdx) : undefined, resolveTarget: resolveTargetIdx > 0 ? this.textureViews.get(resolveTargetIdx) : undefined,
loadOp: this.enumeration("LoadOp", start + 12), depthSlice: depthSlice,
storeOp: this.enumeration("StoreOp", start + 16), loadOp: this.enumeration("LoadOp", start + 16),
storeOp: this.enumeration("StoreOp", start + 20),
clearValue: this.Color(start + 24), clearValue: this.Color(start + 24),
}; };
} }
@@ -950,14 +957,25 @@ class WebGPUInterface {
/** /**
* @param {number} adapterIdx * @param {number} adapterIdx
* @param {number} propertiesPtr * @param {number} infoPtr
*/ */
wgpuAdapterGetProperties: (adapterIdx, propertiesPtr) => { wgpuAdapterGetInfo: (adapterIdx, infoPtr) => {
this.assert(propertiesPtr != 0); this.assert(infoPtr != 0);
// Unknown adapter.
this.mem.storeI32(propertiesPtr + 28, 3);
// WebGPU backend. // 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]); 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 {number} adapterIdx
* @param {0|number} descriptorPtr * @param {0|number} descriptorPtr
@@ -1040,14 +1014,69 @@ class WebGPUInterface {
}; };
} }
let device;
let deviceIdx; let deviceIdx;
try { try {
const device = await adapter.requestDevice(descriptor); device = await adapter.requestDevice(descriptor);
deviceIdx = this.devices.create(device); deviceIdx = this.devices.create(device);
// NOTE: don't callback here, any errors that happen later will then be caught by the catch here. // NOTE: don't callback here, any errors that happen later will then be caught by the catch here.
} catch (e) { } catch (e) {
console.warn(e); const messageLength = new TextEncoder().encode(e.message).length;
callback(1, null, null, userdata); 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); callback(0, deviceIdx, null, userdata);
@@ -1918,29 +1947,6 @@ class WebGPUInterface {
device.pushErrorScope(this.enums.ErrorFilter[filterInt]); 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), ...this.devices.interface(true),
/* ---------------------- Instance ---------------------- */ /* ---------------------- Instance ---------------------- */
@@ -2646,23 +2652,23 @@ class WebGPUInterface {
const formatStr = navigator.gpu.getPreferredCanvasFormat(); const formatStr = navigator.gpu.getPreferredCanvasFormat();
const format = this.enums.TextureFormat.indexOf(formatStr); 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); const formatAddr = this.mem.exports.wgpu_alloc(4);
this.mem.storeI32(formatAddr, format); 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. // 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); const presentModesAddr = this.mem.exports.wgpu_alloc(4);
this.mem.storeI32(presentModesAddr, 0); 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). // 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); const alphaModesAddr = this.mem.exports.wgpu_alloc(8);
this.mem.storeI32(alphaModesAddr + 0, 1); // Opaque. this.mem.storeI32(alphaModesAddr + 0, 1); // Opaque.
this.mem.storeI32(alphaModesAddr + 4, 2); // premultiplied. 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. // 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 * @param {number} surfaceIdx
*/ */
+111 -53
View File
@@ -13,7 +13,7 @@ when ODIN_OS == .Windows {
@(private) LIB :: "lib/wgpu-windows-" + ARCH + "-" + TYPE + "/wgpu_native" + EXT @(private) LIB :: "lib/wgpu-windows-" + ARCH + "-" + TYPE + "/wgpu_native" + EXT
when !#exists(LIB) { 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 { foreign import libwgpu {
@@ -34,7 +34,7 @@ when ODIN_OS == .Windows {
@(private) LIB :: "lib/wgpu-macos-" + ARCH + "-" + TYPE + "/libwgpu_native" + EXT @(private) LIB :: "lib/wgpu-macos-" + ARCH + "-" + TYPE + "/libwgpu_native" + EXT
when !#exists(LIB) { 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 { foreign import libwgpu {
@@ -49,7 +49,7 @@ when ODIN_OS == .Windows {
@(private) LIB :: "lib/wgpu-linux-" + ARCH + "-" + TYPE + "/libwgpu_native" + EXT @(private) LIB :: "lib/wgpu-linux-" + ARCH + "-" + TYPE + "/libwgpu_native" + EXT
when !#exists(LIB) { 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 { foreign import libwgpu {
@@ -220,7 +220,8 @@ CullMode :: enum i32 {
DeviceLostReason :: enum i32 { DeviceLostReason :: enum i32 {
Undefined = 0x00000000, Undefined = 0x00000000,
Destroyed = 0x00000001, Unknown = 0x00000001,
Destroyed = 0x00000002,
} }
ErrorFilter :: enum i32 { ErrorFilter :: enum i32 {
@@ -264,6 +265,30 @@ FeatureName :: enum i32 {
PipelineStatisticsQuery, PipelineStatisticsQuery,
StorageResourceBindingArray, StorageResourceBindingArray,
PartiallyBoundBindingArray, 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 { FilterMode :: enum i32 {
@@ -520,6 +545,18 @@ TextureFormat :: enum i32 {
ASTC12x10UnormSrgb = 0x0000005D, ASTC12x10UnormSrgb = 0x0000005D,
ASTC12x12Unorm = 0x0000005E, ASTC12x12Unorm = 0x0000005E,
ASTC12x12UnormSrgb = 0x0000005F, ASTC12x12UnormSrgb = 0x0000005F,
// Native.
// From FeatureName.TextureFormat16bitNorm
R16Unorm = 0x00030001,
R16Snorm,
Rg16Unorm,
Rg16Snorm,
Rgba16Unorm,
Rgba16Snorm,
// From FeatureName.TextureFormatNv12
NV12,
} }
TextureSampleType :: enum i32 { TextureSampleType :: enum i32 {
@@ -581,13 +618,13 @@ VertexStepMode :: enum i32 {
VertexBufferNotUsed = 0x00000002, VertexBufferNotUsed = 0x00000002,
} }
// WGSLFeatureName :: enum i32 { WGSLFeatureName :: enum i32 {
// Undefined = 0x00000000, Undefined = 0x00000000,
// ReadonlyAndReadwriteStorageTextures = 0x00000001, ReadonlyAndReadwriteStorageTextures = 0x00000001,
// Packed4x8IntegerDotProduct = 0x00000002, Packed4x8IntegerDotProduct = 0x00000002,
// UnrestrictedPointerParameters = 0x00000003, UnrestrictedPointerParameters = 0x00000003,
// PointerCompositeAccess = 0x00000004, PointerCompositeAccess = 0x00000004,
// } }
BufferUsage :: enum i32 { BufferUsage :: enum i32 {
MapRead = 0x00000000, MapRead = 0x00000000,
@@ -634,22 +671,18 @@ TextureUsage :: enum i32 {
} }
TextureUsageFlags :: bit_set[TextureUsage; Flags] TextureUsageFlags :: bit_set[TextureUsage; Flags]
Proc :: distinct rawptr
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)
DeviceLostCallback :: #type proc "c" (reason: DeviceLostReason, message: cstring, userdata: rawptr) DeviceLostCallback :: #type proc "c" (reason: DeviceLostReason, message: cstring, userdata: rawptr)
ErrorCallback :: #type proc "c" (type: ErrorType, 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) AdapterRequestDeviceCallback :: #type proc "c" (status: RequestDeviceStatus, device: Device, message: cstring, /* NULLABLE */ userdata: rawptr)
BufferMapAsyncCallback :: #type proc "c" (status: BufferMapAsyncStatus, /* NULLABLE */ userdata: rawptr)
// AdapterRequestAdapterInfoCallback :: #type proc "c" (adapterInfo: AdapterInfo, /* 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 { ChainedStruct :: struct {
next: ^ChainedStruct, next: ^ChainedStruct,
@@ -661,28 +694,23 @@ ChainedStructOut :: struct {
sType: SType, sType: SType,
} }
// AdapterInfo :: struct { AdapterInfo :: struct {
// next: ^ChainedStructOut,
// vendor: cstring,
// architecture: cstring,
// device: cstring,
// description: cstring,
// backendType: BackendType,
// adapterType: AdapterType,
// vendorID: u32,
// deviceID: u32,
// }
AdapterProperties :: struct {
nextInChain: ^ChainedStructOut, nextInChain: ^ChainedStructOut,
vendorID: u32, vendor: cstring,
vendorName: cstring,
architecture: cstring, architecture: cstring,
deviceID: u32, device: cstring,
name: cstring, description: cstring,
driverDescription: cstring,
adapterType: AdapterType,
backendType: BackendType, 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 { BindGroupEntry :: struct {
@@ -943,6 +971,7 @@ StorageTextureBindingLayout :: struct {
SurfaceCapabilities :: struct { SurfaceCapabilities :: struct {
nextInChain: ^ChainedStructOut, nextInChain: ^ChainedStructOut,
usages: TextureUsageFlags,
formatCount: uint, formatCount: uint,
formats: /* const */ [^]TextureFormat `fmt:"v,formatCount"`, formats: /* const */ [^]TextureFormat `fmt:"v,formatCount"`,
presentModeCount: uint, presentModeCount: uint,
@@ -950,6 +979,16 @@ SurfaceCapabilities :: struct {
alphaModeCount: uint, alphaModeCount: uint,
alphaModes: /* const */ [^]CompositeAlphaMode `fmt:"v,alphaModeCount"`, 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 { SurfaceConfiguration :: struct {
nextInChain: ^ChainedStruct, nextInChain: ^ChainedStruct,
@@ -1040,6 +1079,12 @@ TextureViewDescriptor :: struct {
aspect: TextureAspect, aspect: TextureAspect,
} }
UncapturedErrorCallbackInfo :: struct {
nextInChain: ^ChainedStruct,
callback: ErrorCallback,
userdata: rawptr,
}
VertexAttribute :: struct { VertexAttribute :: struct {
format: VertexFormat, format: VertexFormat,
offset: u64, offset: u64,
@@ -1120,12 +1165,21 @@ ProgrammableStageDescriptor :: struct {
RenderPassColorAttachment :: struct { RenderPassColorAttachment :: struct {
nextInChain: ^ChainedStruct, nextInChain: ^ChainedStruct,
/* NULLABLE */ view: TextureView, /* NULLABLE */ view: TextureView,
// depthSlice: u32, depthSlice: u32,
/* NULLABLE */ resolveTarget: TextureView, /* NULLABLE */ resolveTarget: TextureView,
loadOp: LoadOp, loadOp: LoadOp,
storeOp: StoreOp, storeOp: StoreOp,
clearValue: Color, 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 { RequiredLimits :: struct {
nextInChain: ^ChainedStruct, nextInChain: ^ChainedStruct,
@@ -1194,6 +1248,10 @@ DeviceDescriptor :: struct {
defaultQueue: QueueDescriptor, defaultQueue: QueueDescriptor,
deviceLostCallback: DeviceLostCallback, deviceLostCallback: DeviceLostCallback,
deviceLostUserdata: rawptr, deviceLostUserdata: rawptr,
uncapturedErrorCallbackInfo: UncapturedErrorCallbackInfo,
}
when ODIN_OS == .JS {
#assert(offset_of(DeviceDescriptor, deviceLostCallback) == 24 + size_of(int))
} }
RenderPassDescriptor :: struct { RenderPassDescriptor :: struct {
@@ -1245,16 +1303,18 @@ foreign libwgpu {
// Methods of Adapter // Methods of Adapter
@(link_name="wgpuAdapterEnumerateFeatures") @(link_name="wgpuAdapterEnumerateFeatures")
RawAdapterEnumerateFeatures :: proc(adapter: Adapter, features: [^]FeatureName) -> uint --- RawAdapterEnumerateFeatures :: proc(adapter: Adapter, features: [^]FeatureName) -> uint ---
@(link_name="wgpuAdapterGetInfo")
RawAdapterGetInfo :: proc(adapter: Adapter, info: ^AdapterInfo) ---
@(link_name="wgpuAdapterGetLimits") @(link_name="wgpuAdapterGetLimits")
RawAdapterGetLimits :: proc(adapter: Adapter, limits: ^SupportedLimits) -> b32 --- RawAdapterGetLimits :: proc(adapter: Adapter, limits: ^SupportedLimits) -> b32 ---
@(link_name="wgpuAdapterGetProperties")
RawAdapterGetProperties :: proc(adapter: Adapter, properties: ^AdapterProperties) ---
AdapterHasFeature :: proc(adapter: Adapter, feature: FeatureName) -> b32 --- 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) --- AdapterRequestDevice :: proc(adapter: Adapter, /* NULLABLE */ descriptor: /* const */ ^DeviceDescriptor, callback: AdapterRequestDeviceCallback, /* NULLABLE */ userdata: rawptr = nil) ---
AdapterReference :: proc(adapter: Adapter) --- AdapterReference :: proc(adapter: Adapter) ---
AdapterRelease :: proc(adapter: Adapter) --- AdapterRelease :: proc(adapter: Adapter) ---
// Procs of AdapterInfo
AdapterInfoFreeMembers :: proc(adapterInfo: AdapterInfo) ---
// Methods of BindGroup // Methods of BindGroup
BindGroupSetLabel :: proc(bindGroup: BindGroup, label: cstring) --- BindGroupSetLabel :: proc(bindGroup: BindGroup, label: cstring) ---
BindGroupReference :: proc(bindGroup: BindGroup) --- BindGroupReference :: proc(bindGroup: BindGroup) ---
@@ -1348,13 +1408,12 @@ foreign libwgpu {
DevicePopErrorScope :: proc(device: Device, callback: ErrorCallback, userdata: rawptr) --- DevicePopErrorScope :: proc(device: Device, callback: ErrorCallback, userdata: rawptr) ---
DevicePushErrorScope :: proc(device: Device, filter: ErrorFilter) --- DevicePushErrorScope :: proc(device: Device, filter: ErrorFilter) ---
DeviceSetLabel :: proc(device: Device, label: cstring) --- DeviceSetLabel :: proc(device: Device, label: cstring) ---
DeviceSetUncapturedErrorCallback :: proc(device: Device, callback: ErrorCallback, userdata: rawptr) ---
DeviceReference :: proc(device: Device) --- DeviceReference :: proc(device: Device) ---
DeviceRelease :: proc(device: Device) --- DeviceRelease :: proc(device: Device) ---
// Methods of Instance // Methods of Instance
InstanceCreateSurface :: proc(instance: Instance, descriptor: /* const */ ^SurfaceDescriptor) -> Surface --- 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) --- InstanceProcessEvents :: proc(instance: Instance) ---
InstanceRequestAdapter :: proc(instance: Instance, /* NULLABLE */ options: /* const */ ^RequestAdapterOptions, callback: InstanceRequestAdapterCallback, /* NULLABLE */ userdata: rawptr = nil) --- InstanceRequestAdapter :: proc(instance: Instance, /* NULLABLE */ options: /* const */ ^RequestAdapterOptions, callback: InstanceRequestAdapterCallback, /* NULLABLE */ userdata: rawptr = nil) ---
InstanceReference :: proc(instance: Instance) --- InstanceReference :: proc(instance: Instance) ---
@@ -1455,9 +1514,8 @@ foreign libwgpu {
RawSurfaceGetCapabilities :: proc(surface: Surface, adapter: Adapter, capabilities: ^SurfaceCapabilities) --- RawSurfaceGetCapabilities :: proc(surface: Surface, adapter: Adapter, capabilities: ^SurfaceCapabilities) ---
@(link_name="wgpuSurfaceGetCurrentTexture") @(link_name="wgpuSurfaceGetCurrentTexture")
RawSurfaceGetCurrentTexture :: proc(surface: Surface, surfaceTexture: ^SurfaceTexture) --- RawSurfaceGetCurrentTexture :: proc(surface: Surface, surfaceTexture: ^SurfaceTexture) ---
SurfaceGetPreferredFormat :: proc(surface: Surface, adapter: Adapter) -> TextureFormat ---
SurfacePresent :: proc(surface: Surface) --- SurfacePresent :: proc(surface: Surface) ---
// SurfaceSetLabel :: proc(surface: Surface, label: cstring) --- SurfaceSetLabel :: proc(surface: Surface, label: cstring) ---
SurfaceUnconfigure :: proc(surface: Surface) --- SurfaceUnconfigure :: proc(surface: Surface) ---
SurfaceReference :: proc(surface: Surface) --- SurfaceReference :: proc(surface: Surface) ---
SurfaceRelease :: proc(surface: Surface) --- SurfaceRelease :: proc(surface: Surface) ---
@@ -1500,8 +1558,8 @@ AdapterGetLimits :: proc(adapter: Adapter) -> (limits: SupportedLimits, ok: bool
return return
} }
AdapterGetProperties :: proc(adapter: Adapter) -> (properties: AdapterProperties) { AdapterGetInfo :: proc(adapter: Adapter) -> (info: AdapterInfo) {
RawAdapterGetProperties(adapter, &properties) RawAdapterGetInfo(adapter, &info)
return return
} }
@@ -1634,8 +1692,8 @@ SurfaceGetCurrentTexture :: proc(surface: Surface) -> (surface_texture: SurfaceT
// WGPU Native bindings // WGPU Native bindings
BINDINGS_VERSION :: [4]u8{0, 19, 4, 1} BINDINGS_VERSION :: [4]u8{22, 1, 0, 1}
BINDINGS_VERSION_STRING :: "0.19.4.1" BINDINGS_VERSION_STRING :: "22.1.0.1"
when ODIN_OS != .JS { when ODIN_OS != .JS {
@(private="file", init) @(private="file", init)