Skip to content

Commit

Permalink
wasm: support more vendor libraries
Browse files Browse the repository at this point in the history
Adds support for:
- box2d
- cgltf
- stb image
- stb rect pack
  • Loading branch information
laytan committed Sep 6, 2024
1 parent a99e57c commit 5a432df
Show file tree
Hide file tree
Showing 45 changed files with 2,828 additions and 231 deletions.
4 changes: 2 additions & 2 deletions core/bytes/bytes.odin
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ Inputs:
Returns:
- index: The index of the byte `c`, or -1 if it was not found.
*/
index_byte :: proc(s: []byte, c: byte) -> (index: int) #no_bounds_check {
index_byte :: proc "contextless" (s: []byte, c: byte) -> (index: int) #no_bounds_check {
i, l := 0, len(s)

// Guard against small strings. On modern systems, it is ALWAYS
Expand Down Expand Up @@ -469,7 +469,7 @@ Inputs:
Returns:
- index: The index of the byte `c`, or -1 if it was not found.
*/
last_index_byte :: proc(s: []byte, c: byte) -> int #no_bounds_check {
last_index_byte :: proc "contextless" (s: []byte, c: byte) -> int #no_bounds_check {
i := len(s)

// Guard against small strings. On modern systems, it is ALWAYS
Expand Down
78 changes: 78 additions & 0 deletions core/mem/allocators.odin
Original file line number Diff line number Diff line change
Expand Up @@ -1137,3 +1137,81 @@ buddy_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,

return nil, nil
}

// An allocator that keeps track of allocation sizes and passes it along to resizes.
// This is useful if you are using a library that needs an equivalent of `realloc` but want to use
// the Odin allocator interface.
//
// You want to wrap your allocator into this one if you are trying to use any allocator that relies
// on the old size to work.
//
// The overhead of this allocator is an extra max(alignment, size_of(Header)) bytes allocated for each allocation, these bytes are
// used to store the size and original pointer.
Compat_Allocator :: struct {
parent: Allocator,
}

compat_allocator_init :: proc(rra: ^Compat_Allocator, allocator := context.allocator) {
rra.parent = allocator
}

compat_allocator :: proc(rra: ^Compat_Allocator) -> Allocator {
return Allocator{
data = rra,
procedure = compat_allocator_proc,
}
}

compat_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int,
location := #caller_location) -> (data: []byte, err: Allocator_Error) {
size, old_size := size, old_size

Header :: struct {
size: int,
ptr: rawptr,
}

rra := (^Compat_Allocator)(allocator_data)
switch mode {
case .Alloc, .Alloc_Non_Zeroed:
a := max(alignment, size_of(Header))
size += a
assert(size >= 0, "overflow")

allocation := rra.parent.procedure(rra.parent.data, mode, size, alignment, old_memory, old_size, location) or_return
#no_bounds_check data = allocation[a:]

([^]Header)(raw_data(data))[-1] = {
size = size,
ptr = raw_data(allocation),
}
return

case .Free:
header := ([^]Header)(old_memory)[-1]
return rra.parent.procedure(rra.parent.data, mode, size, alignment, header.ptr, header.size, location)

case .Resize, .Resize_Non_Zeroed:
header := ([^]Header)(old_memory)[-1]

a := max(alignment, size_of(header))
size += a
assert(size >= 0, "overflow")

allocation := rra.parent.procedure(rra.parent.data, mode, size, alignment, header.ptr, header.size, location) or_return
#no_bounds_check data = allocation[a:]

([^]Header)(raw_data(data))[-1] = {
size = size,
ptr = raw_data(allocation),
}
return

case .Free_All, .Query_Info, .Query_Features:
return rra.parent.procedure(rra.parent.data, mode, size, alignment, old_memory, old_size, location)

case: unreachable()
}
}
89 changes: 19 additions & 70 deletions core/os/os_js.odin
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,38 @@ package os

import "base:runtime"

foreign import "odin_env"

@(require_results)
is_path_separator :: proc(c: byte) -> bool {
return c == '/' || c == '\\'
}

Handle :: distinct u32

stdout: Handle = 1
stderr: Handle = 2

@(require_results)
open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Error) {
unimplemented("core:os procedure not supported on JS target")
}

close :: proc(fd: Handle) -> Error {
unimplemented("core:os procedure not supported on JS target")
return nil
}

flush :: proc(fd: Handle) -> (err: Error) {
unimplemented("core:os procedure not supported on JS target")
return nil
}



write :: proc(fd: Handle, data: []byte) -> (int, Error) {
unimplemented("core:os procedure not supported on JS target")
}

@(private="file")
read_console :: proc(handle: Handle, b: []byte) -> (n: int, err: Error) {
unimplemented("core:os procedure not supported on JS target")
foreign odin_env {
@(link_name="write")
_write :: proc "contextless" (fd: Handle, p: []byte) ---
}
_write(fd, data)
return len(data), nil
}

read :: proc(fd: Handle, data: []byte) -> (int, Error) {
Expand All @@ -45,36 +50,13 @@ file_size :: proc(fd: Handle) -> (i64, Error) {
unimplemented("core:os procedure not supported on JS target")
}


@(private)
MAX_RW :: 1<<30

@(private)
pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) {
unimplemented("core:os procedure not supported on JS target")
}
@(private)
pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) {
unimplemented("core:os procedure not supported on JS target")
}

read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
unimplemented("core:os procedure not supported on JS target")
}
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
unimplemented("core:os procedure not supported on JS target")
}

stdout: Handle = 1
stderr: Handle = 2

@(require_results)
get_std_handle :: proc "contextless" (h: uint) -> Handle {
context = runtime.default_context()
unimplemented("core:os procedure not supported on JS target")
}


@(require_results)
exists :: proc(path: string) -> bool {
unimplemented("core:os procedure not supported on JS target")
Expand All @@ -90,9 +72,6 @@ is_dir :: proc(path: string) -> bool {
unimplemented("core:os procedure not supported on JS target")
}

// NOTE(tetra): GetCurrentDirectory is not thread safe with SetCurrentDirectory and GetFullPathName
//@private cwd_lock := win32.SRWLOCK{} // zero is initialized

@(require_results)
get_current_directory :: proc(allocator := context.allocator) -> string {
unimplemented("core:os procedure not supported on JS target")
Expand All @@ -118,18 +97,6 @@ remove_directory :: proc(path: string) -> (err: Error) {
}



@(private, require_results)
is_abs :: proc(path: string) -> bool {
unimplemented("core:os procedure not supported on JS target")
}

@(private, require_results)
fix_long_path :: proc(path: string) -> string {
unimplemented("core:os procedure not supported on JS target")
}


link :: proc(old_name, new_name: string) -> (err: Error) {
unimplemented("core:os procedure not supported on JS target")
}
Expand Down Expand Up @@ -169,7 +136,6 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
unimplemented("core:os procedure not supported on JS target")
}

Handle :: distinct uintptr
File_Time :: distinct u64

_Platform_Error :: enum i32 {
Expand Down Expand Up @@ -254,12 +220,7 @@ WSAECONNRESET :: Platform_Error.WSAECONNRESET
ERROR_FILE_IS_PIPE :: General_Error.File_Is_Pipe
ERROR_FILE_IS_NOT_DIR :: General_Error.Not_Dir

// "Argv" arguments converted to Odin strings
args := _alloc_command_line_arguments()




args: []string

@(require_results)
last_write_time :: proc(fd: Handle) -> (File_Time, Error) {
Expand All @@ -279,26 +240,14 @@ get_page_size :: proc() -> int {

@(private, require_results)
_processor_core_count :: proc() -> int {
unimplemented("core:os procedure not supported on JS target")
return 1
}

exit :: proc "contextless" (code: int) -> ! {
context = runtime.default_context()
unimplemented("core:os procedure not supported on JS target")
unimplemented_contextless("core:os procedure not supported on JS target")
}



@(require_results)
current_thread_id :: proc "contextless" () -> int {
context = runtime.default_context()
unimplemented("core:os procedure not supported on JS target")
return 0
}



@(require_results)
_alloc_command_line_arguments :: proc() -> []string {
return nil
}

14 changes: 7 additions & 7 deletions core/strings/strings.odin
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ Inputs:
Returns:
- res: A string created from the null-terminated byte pointer and length
*/
string_from_null_terminated_ptr :: proc(ptr: [^]byte, len: int) -> (res: string) {
string_from_null_terminated_ptr :: proc "contextless" (ptr: [^]byte, len: int) -> (res: string) {
s := string(ptr[:len])
s = truncate_to_byte(s, 0)
return s
Expand Down Expand Up @@ -139,7 +139,7 @@ NOTE: Failure to find the byte results in returning the entire string.
Returns:
- res: The truncated string
*/
truncate_to_byte :: proc(str: string, b: byte) -> (res: string) {
truncate_to_byte :: proc "contextless" (str: string, b: byte) -> (res: string) {
n := index_byte(str, b)
if n < 0 {
n = len(str)
Expand Down Expand Up @@ -261,7 +261,7 @@ Inputs:
Returns:
- result: `-1` if `lhs` comes first, `1` if `rhs` comes first, or `0` if they are equal
*/
compare :: proc(lhs, rhs: string) -> (result: int) {
compare :: proc "contextless" (lhs, rhs: string) -> (result: int) {
return mem.compare(transmute([]byte)lhs, transmute([]byte)rhs)
}
/*
Expand Down Expand Up @@ -1447,7 +1447,7 @@ Output:
-1
*/
index_byte :: proc(s: string, c: byte) -> (res: int) {
index_byte :: proc "contextless" (s: string, c: byte) -> (res: int) {
return #force_inline bytes.index_byte(transmute([]u8)s, c)
}
/*
Expand Down Expand Up @@ -1482,7 +1482,7 @@ Output:
-1
*/
last_index_byte :: proc(s: string, c: byte) -> (res: int) {
last_index_byte :: proc "contextless" (s: string, c: byte) -> (res: int) {
return #force_inline bytes.last_index_byte(transmute([]u8)s, c)
}
/*
Expand Down Expand Up @@ -1576,8 +1576,8 @@ Output:
-1
*/
index :: proc(s, substr: string) -> (res: int) {
hash_str_rabin_karp :: proc(s: string) -> (hash: u32 = 0, pow: u32 = 1) {
index :: proc "contextless" (s, substr: string) -> (res: int) {
hash_str_rabin_karp :: proc "contextless" (s: string) -> (hash: u32 = 0, pow: u32 = 1) {
for i := 0; i < len(s); i += 1 {
hash = hash*PRIME_RABIN_KARP + u32(s[i])
}
Expand Down
22 changes: 18 additions & 4 deletions vendor/box2d/box2d.odin
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ package vendor_box2d
import "base:intrinsics"
import "core:c"

@(private) VECTOR_EXT :: "avx2" when #config(VENDOR_BOX2D_ENABLE_AVX2, intrinsics.has_target_feature("avx2")) else "sse2"
when ODIN_ARCH == .amd64 {
@(private) VECTOR_EXT :: "avx2" when #config(VENDOR_BOX2D_ENABLE_AVX2, intrinsics.has_target_feature("avx2")) else "sse2"
} else {
@(private) VECTOR_EXT :: "_simd" when #config(VENDOR_BOX2D_ENABLE_SIMD128, intrinsics.has_target_feature("simd128")) else ""
}

when ODIN_OS == .Windows {
@(private) LIB_PATH :: "lib/box2d_windows_amd64_" + VECTOR_EXT + ".lib"
Expand All @@ -13,6 +17,8 @@ when ODIN_OS == .Windows {
@(private) LIB_PATH :: "lib/box2d_darwin_amd64_" + VECTOR_EXT + ".a"
} else when ODIN_ARCH == .amd64 {
@(private) LIB_PATH :: "lib/box2d_other_amd64_" + VECTOR_EXT + ".a"
} else when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 {
@(private) LIB_PATH :: "lib/box2d_wasm" + VECTOR_EXT + ".o"
} else {
@(private) LIB_PATH :: "lib/box2d_other.a"
}
Expand All @@ -21,8 +27,16 @@ when !#exists(LIB_PATH) {
#panic("Could not find the compiled box2d libraries at \"" + LIB_PATH + "\", they can be compiled by running the `build.sh` script at `" + ODIN_ROOT + "vendor/box2d/build_box2d.sh\"`")
}

foreign import lib {
LIB_PATH,
when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 {
when VECTOR_EXT == "_simd" {
foreign import lib "lib/box2d_wasm_simd.o"
} else {
foreign import lib "lib/box2d_wasm.o"
}
} else {
foreign import lib {
LIB_PATH,
}
}


Expand Down Expand Up @@ -1520,4 +1534,4 @@ IsValid :: proc{
Joint_IsValid,

IsValidRay,
}
}
4 changes: 4 additions & 0 deletions vendor/box2d/box2d_wasm.odin
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
//+build wasm32, wasm64p32
package vendor_box2d

@(require) import _ "vendor:libc"
2 changes: 2 additions & 0 deletions vendor/box2d/build_box2d.sh
Original file line number Diff line number Diff line change
Expand Up @@ -68,5 +68,7 @@ esac

cd ..

make -f wasm.Makefile

rm -rf v3.0.0.tar.gz
rm -rf box2d-3.0.0
Binary file added vendor/box2d/lib/box2d_wasm.o
Binary file not shown.
Binary file added vendor/box2d/lib/box2d_wasm_simd.o
Binary file not shown.
Loading

0 comments on commit 5a432df

Please sign in to comment.